home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / DMO / GargleDMO / DMOBase / dmobase.h
Encoding:
C/C++ Source or Header  |  2001-10-11  |  87.1 KB  |  2,626 lines

  1. //------------------------------------------------------------------------------
  2. // File: DMOBase.h
  3. //
  4. // Desc: A collection of DMO base classes.
  5. //
  6. // Copyright (c) 1999-2001 Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9.  
  10. // Current hierarchy:
  11. //
  12. //   IMediaObject
  13. //   |
  14. //   +-- C1in1outDMO - generic base class for DMOs with 1 in and 1 out
  15. //   |   |
  16. //   |   +-- FBRDMO - base class for fixed sample size, fixed bitrate DMOs
  17. //   |   |   |
  18. //   |   |   +-- CPCMDMO - base class for PCM audio DMOs
  19. //   |   |
  20. //   |   +-- C1for1DMO - base class for single sample per buffer 1-in/1-out DMOs
  21. //   |       |
  22. //   |       +-- C1for1QCDMO - adds IDMOQualityControl to C1for1DMO
  23. //   |
  24. //   +-- CGenericDMO - resonably generic base class for multi-input/output DMOs
  25. //
  26.  
  27. #ifndef __DMOBASE_H_
  28. #define __DMOBASE_H_
  29.  
  30. #include "dmo.h"
  31. #include "assert.h"
  32. #include "math.h"
  33.  
  34. //
  35. // locking helper class
  36. //
  37. #ifdef DMO_NOATL
  38. class CDMOAutoLock {
  39. public:
  40.    CDMOAutoLock(CRITICAL_SECTION* pcs)
  41.       : m_pcs(pcs)
  42.    {
  43.       EnterCriticalSection(m_pcs);
  44.    }
  45.    ~CDMOAutoLock() {
  46.       LeaveCriticalSection(m_pcs);
  47.    }
  48. private:
  49.    CRITICAL_SECTION* m_pcs;
  50. };
  51. #else
  52. class CDMOAutoLock {
  53. public:
  54.    CDMOAutoLock(CComAutoCriticalSection* pcs)
  55.       : m_pcs(pcs)
  56.    {
  57.       m_pcs->Lock();
  58.    }
  59.    ~CDMOAutoLock() {
  60.       m_pcs->Unlock();
  61.    }
  62. private:
  63.    CComAutoCriticalSection* m_pcs;
  64. };
  65. #endif
  66.  
  67.  
  68. //
  69. // C1in1outDMO - generic base class for 1-input/1-output DMOs.
  70. //
  71. //
  72. //
  73. // C1in1outDMO implements all IMediaObject methods.  The derived class
  74. // customizes the DMO's behavior by overriding some or all of the following
  75. // virtual functions:
  76. //
  77. // Main Streaming:
  78. //    AcceptInput          // accept one new input buffer
  79. //    ProduceOutput        // fill up one output buffer with new data
  80. //    AcceptingInput       // check if DMO is ready for new input
  81. // Other streaming:
  82. //    PrepareForStreaming  // hook called after both types have been set
  83. //    Discontinuity        // notify DMO of a discontinuity
  84. //    DoFlush              // discard all data and start anew
  85. // Mediatype negotiation:
  86. //    GetInputType         // input type enumerator
  87. //    GetOutputType        // output type enumerator
  88. //    CheckInputType       // verifies proposed input type is acceptable
  89. //    CheckOutputType      // verifies proposed output type is acceptable
  90. // Buffer size negotiation:
  91. //    GetInputFlags        // input data flow flags
  92. //    GetOutputFlags       // output fata flow flags
  93. //    GetInputSizeInfo     // input buffer size requirements
  94. //    GetOutputSizeInfo    // output buffer size requirements
  95. //
  96. // This base class assumes that the derived class will not override any
  97. // IMediaObject methods directly - the derived class should override the
  98. // methods listed above instead.
  99. //
  100. //
  101. //
  102. // The base class provides a default implementation for each of the
  103. // overridables listed above.  However, to make a useful DMO the derived class
  104. // probably needs to override at least the following two methods:
  105. //
  106. //    HRESULT AcceptingInput();
  107. //    HRESULT AcceptInput(BYTE* pData,
  108. //                        ULONG ulSize,
  109. //                        DWORD dwFlags,
  110. //                        REFERENCE_TIME rtTimestamp,
  111. //                        REFERENCE_TIME rtTimelength,
  112. //                        IMediaBuffer* pMediaBuffer);
  113. //    HRESULT ProduceOutput(BYTE *pData,
  114. //                        ULONG ulAvail,
  115. //                        ULONG* pulUsed,
  116. //                        DWORD* pdwStatus,
  117. //                        REFERENCE_TIME *prtTimestamp,
  118. //                        REFERENCE_TIME *prtTimelength);
  119. //
  120. // All good DMOs should also override these (the default implementation
  121. // simply accepts any mediatype, which in general is not good DMO behavior):
  122. //
  123. //    HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  124. //    HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  125. //    HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt);
  126. //    HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt);
  127. //
  128. // DMOs that store data and/or state information may need to implement
  129. //
  130. //    HRESULT PrepareForStreaming();
  131. //    HRESULT Discontinuity();
  132. //    HRESULT Flush();
  133. //
  134. // Finally, DMOs that make any buffer size assumptions will need to override
  135. // these:
  136. //
  137. //    HRESULT GetInputFlags(DWORD* pdwFlags);
  138. //    HRESULT GetOutputFlags(DWORD* pdwFlags);
  139. //    HRESULT GetInputSizeInfo(ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment);
  140. //    HRESULT GetOutputSizeInfo(ULONG *pulSize, ULONG *pulAlignment);
  141. //
  142. //
  143. //
  144. // The following functions are provided by this base class exclusively for use
  145. // by the derived class.  The derived class should call these to find out the
  146. // currently set mediatype(s) whenever it needs to make a decision that
  147. // depends on the mediatype used.  Each of these returns NULL if the mediatype
  148. // has not been set yet.
  149. //
  150. //    DMO_MEDIA_TYPE *InputType();
  151. //    DMO_MEDIA_TYPE *OutputType().
  152. //
  153.  
  154. #define PROLOGUE \
  155.     CDMOAutoLock l(&m_cs); \
  156.     if (ulStreamIndex >= 1) \
  157.        return DMO_E_INVALIDSTREAMINDEX
  158.  
  159. class C1in1outDMO : public IMediaObject
  160. {
  161. public:
  162.     C1in1outDMO() :
  163.        m_bInputTypeSet(FALSE),
  164.        m_bOutputTypeSet(FALSE),
  165.        m_bIncomplete(FALSE)
  166.     {
  167. #ifdef DMO_NOATL
  168.        InitializeCriticalSection(&m_cs);
  169. #endif
  170.     }
  171.     ~C1in1outDMO() {
  172.  
  173.        FreeInputType();
  174.        FreeOutputType();
  175.  
  176. #ifdef DMO_NOATL
  177.        DeleteCriticalSection(&m_cs);
  178. #endif
  179.     }
  180.  
  181. public:
  182.     //
  183.     // IMediaObject methods
  184.     //
  185.     STDMETHODIMP GetStreamCount(unsigned long *pulNumberOfInputStreams, unsigned long *pulNumberOfOutputStreams)
  186.     {
  187.         CDMOAutoLock l(&m_cs);
  188.         if (pulNumberOfInputStreams == NULL ||
  189.             pulNumberOfOutputStreams == NULL) {
  190.             return E_POINTER;
  191.         }
  192.         *pulNumberOfInputStreams = 1;
  193.         *pulNumberOfOutputStreams = 1;
  194.         return S_OK;
  195.     }
  196.     STDMETHODIMP GetInputStreamInfo(ULONG ulStreamIndex, DWORD *pdwFlags)
  197.     {
  198.        if( NULL == pdwFlags ) {
  199.           return E_POINTER;
  200.        }
  201.  
  202.        PROLOGUE;
  203.        return GetInputFlags(pdwFlags);
  204.     }
  205.     STDMETHODIMP GetOutputStreamInfo(ULONG ulStreamIndex, DWORD *pdwFlags)
  206.     {
  207.        if( NULL == pdwFlags ) {
  208.           return E_POINTER;
  209.        }
  210.  
  211.        PROLOGUE;
  212.        return GetOutputFlags(pdwFlags);
  213.     }
  214.     STDMETHODIMP GetInputType(ULONG ulStreamIndex, ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  215.        PROLOGUE;
  216.        return GetInputType(ulTypeIndex, pmt);
  217.     }
  218.     STDMETHODIMP GetOutputType(ULONG ulStreamIndex, ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  219.        PROLOGUE;
  220.        return GetOutputType(ulTypeIndex, pmt);
  221.     }
  222.     STDMETHODIMP GetInputCurrentType(ULONG ulStreamIndex, DMO_MEDIA_TYPE *pmt) {
  223.        PROLOGUE;
  224.  
  225.        if (m_bInputTypeSet)
  226.            return MoCopyMediaType(pmt, &m_InputType);
  227.        else
  228.           return DMO_E_TYPE_NOT_SET;
  229.     }
  230.     STDMETHODIMP GetOutputCurrentType(ULONG ulStreamIndex, DMO_MEDIA_TYPE *pmt) {
  231.        PROLOGUE;
  232.  
  233.        if (m_bOutputTypeSet)
  234.            return MoCopyMediaType(pmt, &m_OutputType);
  235.        else
  236.           return DMO_E_TYPE_NOT_SET;
  237.     }
  238.     STDMETHODIMP GetInputSizeInfo(ULONG ulStreamIndex, ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment) {
  239.         
  240.        if( (NULL == pulSize) || (NULL == pcbMaxLookahead) || (NULL == pulAlignment) ) {
  241.           return E_POINTER;
  242.        }
  243.  
  244.        PROLOGUE;
  245.  
  246.        if (!m_bInputTypeSet)
  247.           return DMO_E_TYPE_NOT_SET;
  248.        return GetInputSizeInfo(pulSize, pcbMaxLookahead, pulAlignment);
  249.     }
  250.     STDMETHODIMP GetOutputSizeInfo(ULONG ulStreamIndex, ULONG *pulSize, ULONG *pulAlignment) {
  251.  
  252.        if( (NULL == pulSize) || (NULL == pulAlignment) ) {
  253.           return E_POINTER;
  254.        }
  255.  
  256.        PROLOGUE;
  257.  
  258.        if (!m_bOutputTypeSet)
  259.           return DMO_E_TYPE_NOT_SET;
  260.        return GetOutputSizeInfo(pulSize, pulAlignment);
  261.     }
  262.     STDMETHODIMP SetInputType(ULONG ulStreamIndex, const DMO_MEDIA_TYPE *pmt, DWORD dwFlags) {
  263.  
  264.        PROLOGUE;
  265.  
  266.        HRESULT hr = ValidateSetTypeParameters(pmt, dwFlags);
  267.        if (FAILED(hr)) {
  268.           return hr;
  269.        }
  270.  
  271.        if (DMO_SET_TYPEF_CLEAR & dwFlags) {
  272.           FreeInputType();
  273.           return NOERROR;
  274.        } else {
  275.           hr = CheckInputType(pmt);
  276.           if (FAILED(hr))
  277.              return hr;
  278.  
  279.           if (dwFlags & DMO_SET_TYPEF_TEST_ONLY)
  280.              return NOERROR;
  281.  
  282.           hr = AtomicCopyMediaType(pmt, &m_InputType, m_bInputTypeSet);
  283.           if (FAILED(hr)) {
  284.              return hr;
  285.           }
  286.  
  287.           m_bInputTypeSet = TRUE;
  288.  
  289.           if (m_bOutputTypeSet) {
  290.              hr = PrepareForStreaming();
  291.              if (FAILED(hr)) {
  292.                 FreeInputType();
  293.                 return hr;
  294.              }
  295.           }
  296.  
  297.           return NOERROR;
  298.        }
  299.     }
  300.     STDMETHODIMP SetOutputType(ULONG ulStreamIndex, const DMO_MEDIA_TYPE *pmt, DWORD dwFlags) {
  301.         
  302.        PROLOGUE;
  303.  
  304.        HRESULT hr = ValidateSetTypeParameters(pmt, dwFlags);
  305.        if (FAILED(hr)) {
  306.           return hr;
  307.        }
  308.  
  309.        if (DMO_SET_TYPEF_CLEAR & dwFlags) {
  310.           FreeOutputType();
  311.           return NOERROR;
  312.        } else {
  313.            hr = CheckOutputType(pmt);
  314.            if (FAILED(hr))
  315.               return hr;
  316.  
  317.            if (dwFlags & DMO_SET_TYPEF_TEST_ONLY)
  318.               return NOERROR;
  319.  
  320.            hr = AtomicCopyMediaType(pmt, &m_OutputType, m_bOutputTypeSet);
  321.            if (FAILED(hr)) {
  322.               return hr;
  323.            }
  324.  
  325.            m_bOutputTypeSet = TRUE;
  326.  
  327.            if (m_bInputTypeSet) {
  328.               hr = PrepareForStreaming();
  329.               if (FAILED(hr)) {
  330.                  FreeOutputType();
  331.                  return hr;
  332.               }
  333.            }
  334.  
  335.            return NOERROR;
  336.        }
  337.     }
  338.     STDMETHODIMP GetInputStatus(
  339.         ULONG ulStreamIndex,
  340.         DWORD *pdwStatus
  341.     ) {
  342.  
  343.        if( NULL == pdwStatus ) {
  344.           return E_POINTER;
  345.        }
  346.  
  347.        PROLOGUE;
  348.  
  349.        *pdwStatus = 0;
  350.        if (AcceptingInput() == S_OK)
  351.           *pdwStatus |= DMO_INPUT_STATUSF_ACCEPT_DATA;
  352.        return NOERROR;
  353.  
  354.     }
  355.     STDMETHODIMP GetInputMaxLatency(unsigned long ulStreamIndex, REFERENCE_TIME *prtLatency) {
  356.        return E_NOTIMPL;
  357.     }
  358.     STDMETHODIMP SetInputMaxLatency(unsigned long ulStreamIndex, REFERENCE_TIME rtLatency) {
  359.        return E_NOTIMPL;
  360.     }
  361.     STDMETHODIMP Discontinuity(ULONG ulStreamIndex) {
  362.        PROLOGUE;
  363.        return Discontinuity();
  364.     }
  365.  
  366.     STDMETHODIMP Flush()
  367.     {
  368.        CDMOAutoLock l(&m_cs);
  369.        DoFlush();
  370.        return NOERROR;
  371.     }
  372.     STDMETHODIMP AllocateStreamingResources() {return S_OK;}
  373.     STDMETHODIMP FreeStreamingResources() {return S_OK;}
  374.  
  375.     //
  376.     // Processing methods - public entry points
  377.     //
  378.     STDMETHODIMP ProcessInput(
  379.         DWORD ulStreamIndex,
  380.         IMediaBuffer *pBuffer, // [in], must not be NULL
  381.         DWORD dwFlags, // [in] - discontinuity, timestamp, etc.
  382.         REFERENCE_TIME rtTimestamp, // [in], valid if flag set
  383.         REFERENCE_TIME rtTimelength // [in], valid if flag set
  384.     ) {
  385.        PROLOGUE;
  386.        if (!TypesSet()) {
  387.           return DMO_E_TYPE_NOT_SET;
  388.        }
  389.        if (AcceptingInput() != S_OK)
  390.           return DMO_E_NOTACCEPTING;
  391.        if (!pBuffer)
  392.           return E_POINTER;
  393.  
  394.        // deal with the IMediaBuffer so the derived class doesn't have to
  395.        BYTE *pData;
  396.        ULONG ulSize;
  397.        HRESULT hr = pBuffer->GetBufferAndLength(&pData, &ulSize);
  398.        if (FAILED(hr))
  399.           return hr;
  400.        if (pData == NULL)
  401.           ulSize = 0;
  402.  
  403.        m_bIncomplete = TRUE; // new input means we may be able to produce output
  404.  
  405.        return AcceptInput(pData, ulSize, dwFlags, rtTimestamp, rtTimelength, pBuffer);
  406.     }
  407.  
  408.     STDMETHODIMP ProcessOutput(
  409.                     DWORD dwReserved,
  410.                     DWORD ulOutputBufferCount,
  411.                     DMO_OUTPUT_DATA_BUFFER *pOutputBuffers,
  412.                     DWORD *pdwStatus)
  413.     {
  414.        HRESULT hr;
  415.        CDMOAutoLock l(&m_cs);
  416.  
  417.        if (pdwStatus == NULL) {
  418.            return E_POINTER;
  419.        }
  420.  
  421.        *pdwStatus = 0;
  422.  
  423.        if (ulOutputBufferCount != 1)
  424.           return E_INVALIDARG;
  425.  
  426.        if (!TypesSet()) {
  427.           return DMO_E_TYPE_NOT_SET;
  428.        }
  429.  
  430.        pOutputBuffers[0].dwStatus = 0;
  431.  
  432.        // deal with the IMediaBuffer so the derived class doesn't have to
  433.        BYTE *pOut;
  434.        ULONG ulSize=0;
  435.        ULONG ulAvail;
  436.  
  437.        if (pOutputBuffers[0].pBuffer) {
  438.            hr = pOutputBuffers[0].pBuffer->GetBufferAndLength(&pOut, &ulSize);
  439.            if (FAILED(hr)) return hr;
  440.            hr = pOutputBuffers[0].pBuffer->GetMaxLength(&ulAvail);
  441.            if (FAILED(hr)) return hr;
  442.  
  443.            if (ulSize) { // skip any already used portion of the buffer
  444.               if (ulSize > ulAvail)
  445.                  return E_INVALIDARG;
  446.               ulAvail -= ulSize;
  447.               pOut += ulSize;
  448.            }
  449.        }
  450.        else { // no IMediaBuffer
  451.            //
  452.            // If (a) the output stream says it can operate without buffers, AND
  453.            //    (b) the DISCARD flag was set in dwReserved,
  454.            // then call ProduceOutput with a NULL output buffer pointer.
  455.            //
  456.            // Otherwise just return the INCOMPLETE flag without any processing.
  457.            //
  458.            DWORD dwFlags;
  459.            if (SUCCEEDED(GetOutputFlags(&dwFlags)) &&
  460.                ((dwFlags & DMO_OUTPUT_STREAMF_DISCARDABLE) ||
  461.                 (dwFlags & DMO_OUTPUT_STREAMF_OPTIONAL)
  462.                ) &&
  463.                (dwReserved & DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER))
  464.            { // process, but discard the output
  465.                pOut = NULL;
  466.                ulAvail = 0;
  467.            }
  468.            else { // just report the incomplete status without altering our state
  469.               if (m_bIncomplete)
  470.                  pOutputBuffers[0].dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
  471.               return NOERROR;
  472.            }
  473.        }
  474.  
  475.        ULONG ulProduced = 0;
  476.        hr = ProduceOutput(pOut,
  477.                           ulAvail,
  478.                           &ulProduced,
  479.                           &(pOutputBuffers[0].dwStatus),
  480.                           &(pOutputBuffers[0].rtTimestamp),
  481.                           &(pOutputBuffers[0].rtTimelength));
  482.        if (FAILED(hr))
  483.           return hr;
  484.  
  485.        HRESULT hrProcess = hr; // remember this in case it's S_FALSE
  486.  
  487.        // remember the DMO's incomplete status
  488.        if (pOutputBuffers[0].dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE)
  489.           m_bIncomplete = TRUE;
  490.        else
  491.           m_bIncomplete = FALSE;
  492.  
  493.        if (pOut) { // if using an output buffer, set the amount we used
  494.            if (ulProduced > ulAvail)
  495.               return E_FAIL;
  496.  
  497.            hr = pOutputBuffers[0].pBuffer->SetLength(ulSize + ulProduced);
  498.            if (FAILED(hr))
  499.               return hr;
  500.        }
  501.  
  502.        return hrProcess;
  503.     }
  504. #ifdef FIX_LOCK_NAME
  505.     STDMETHODIMP DMOLock(LONG lLock)
  506. #else
  507.     STDMETHODIMP Lock(LONG lLock)
  508. #endif
  509.     {
  510.         if (lLock) {
  511. #ifdef DMO_NOATL
  512.             EnterCriticalSection(&m_cs);
  513. #else
  514.             m_cs.Lock();
  515. #endif
  516.         } 
  517.         else 
  518.         {
  519. #ifdef DMO_NOATL
  520.             LeaveCriticalSection(&m_cs);
  521. #else
  522.             m_cs.Unlock();
  523. #endif
  524.         }
  525.         return S_OK;
  526.     }
  527.  
  528. protected:
  529.     HRESULT AtomicCopyMediaType(const DMO_MEDIA_TYPE *pmtSource, DMO_MEDIA_TYPE *pmtDestination, BOOL bDestinationInitialized) {
  530.  
  531.        // pmtDestination should always point to a valid DMO_MEDIA_TYPE structure.
  532.        assert(NULL != pmtDestination);
  533.  
  534.        DMO_MEDIA_TYPE mtTempDestination;
  535.  
  536.        // actually set the type
  537.        HRESULT hr = MoCopyMediaType(&mtTempDestination, pmtSource);
  538.        if (FAILED(hr)) {
  539.           return hr;
  540.        }
  541.  
  542.        // Free any previous mediatype
  543.        if (bDestinationInitialized) {
  544.           MoFreeMediaType(pmtDestination);
  545.        }
  546.  
  547.        *pmtDestination = mtTempDestination;
  548.  
  549.         return S_OK;
  550.     }
  551.  
  552.     //
  553.     // private methods for use by derived class
  554.     //
  555.     DMO_MEDIA_TYPE *InputType() {
  556.        if (m_bInputTypeSet)
  557.           return &m_InputType;
  558.        else
  559.           return NULL;
  560.     }
  561.     DMO_MEDIA_TYPE *OutputType() {
  562.        if (m_bOutputTypeSet)
  563.           return &m_OutputType;
  564.        else
  565.           return NULL;
  566.     }
  567.  
  568. protected:
  569.     //
  570.     // To be overriden by the derived class
  571.     //
  572.     virtual HRESULT GetInputFlags(DWORD* pdwFlags) {
  573.        *pdwFlags = 0; // default implementation assumes no lookahead
  574.        return NOERROR;
  575.     }
  576.     virtual HRESULT GetOutputFlags(DWORD* pdwFlags) {
  577.        *pdwFlags = 0;
  578.        return NOERROR;
  579.     }
  580.  
  581.     virtual HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  582.        return DMO_E_NO_MORE_ITEMS; // default implementation exposes no types
  583.     }
  584.     virtual HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  585.        return DMO_E_NO_MORE_ITEMS; // default implementation exposes no types
  586.     }
  587.     virtual HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt) {
  588.        if ((pmt == NULL) || ((pmt->cbFormat > 0) && (pmt->pbFormat == NULL)))
  589.           return E_POINTER;
  590.        return S_OK; // default implementation accepts anything
  591.     }
  592.     virtual HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt) {
  593.        if ((pmt == NULL) || ((pmt->cbFormat > 0) && (pmt->pbFormat == NULL)))
  594.           return E_POINTER;
  595.        return S_OK; // default implementation accepts anything
  596.     }
  597.  
  598.     virtual HRESULT GetInputSizeInfo(ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment) {
  599.        *pulSize = 1; // default implementation imposes no size requirements
  600.        *pcbMaxLookahead = 0; // default implementation assumes no lookahead
  601.        *pulAlignment = 1; // default implementation assumes no alignment
  602.        return NOERROR;
  603.     }
  604.     virtual HRESULT GetOutputSizeInfo(ULONG *pulSize, ULONG *pulAlignment) {
  605.        *pulSize = 1; // default implementation imposes no size requirements
  606.        *pulAlignment = 1; // default implementation assumes no alignment
  607.        return NOERROR;
  608.     }
  609.  
  610.     virtual HRESULT PrepareForStreaming() {
  611.        return NOERROR;
  612.     }
  613.     virtual HRESULT AcceptingInput() {
  614.        return S_FALSE;
  615.     }
  616.     virtual HRESULT Discontinuity() {
  617.        return NOERROR;
  618.     }
  619.     virtual HRESULT DoFlush() {
  620.        return NOERROR;
  621.     }
  622.  
  623.     virtual HRESULT AcceptInput(BYTE* pData,
  624.                                 ULONG ulSize,
  625.                                 DWORD dwFlags,
  626.                                 REFERENCE_TIME rtTimestamp,
  627.                                 REFERENCE_TIME rtTimelength,
  628.                                 IMediaBuffer* pMediaBuffer
  629.     ) {
  630.        m_bIncomplete = FALSE;
  631.        return S_FALSE;
  632.     }
  633.     virtual HRESULT ProduceOutput(BYTE *pData,
  634.                                   ULONG ulAvail,
  635.                                   ULONG* pulUsed,
  636.                                   DWORD* pdwStatus,
  637.                                   REFERENCE_TIME *prtTimestamp,
  638.                                   REFERENCE_TIME *prtTimelength
  639.     ) {
  640.        *pulUsed = 0;
  641.        return S_FALSE;
  642.     }
  643.  
  644.     HRESULT ValidateSetTypeParameters(const DMO_MEDIA_TYPE *pmt, DWORD dwFlags)
  645.     {
  646.        // Validate parameters.
  647.        if (!(DMO_SET_TYPEF_CLEAR & dwFlags)) {
  648.           // The DMO specification states that pmt CANNOT be NULL if
  649.           // the DMO_SET_TYPEF_CLEAR flag is NOT set.
  650.           if (NULL == pmt) {
  651.              return E_POINTER;
  652.           }
  653.        }
  654.  
  655.        // The caller cannot set the DMO_SET_TYPEF_CLEAR flag and the 
  656.        // DMO_SET_TYPEF_TEST_ONLY flag.  The DMO specification prohibits 
  657.        // this combination because the two flags are mutually exclusive.
  658.        if ((DMO_SET_TYPEF_CLEAR & dwFlags) && (DMO_SET_TYPEF_TEST_ONLY & dwFlags)) {
  659.           return E_INVALIDARG;
  660.        }
  661.  
  662.        // Check for illegal flags.
  663.        if (~(DMO_SET_TYPEF_CLEAR | DMO_SET_TYPEF_TEST_ONLY) & dwFlags) {
  664.             return E_INVALIDARG;
  665.        }
  666.  
  667.        return S_OK;
  668.     }
  669.  
  670.     bool TypesSet() {
  671.         return m_bInputTypeSet && m_bOutputTypeSet;
  672.     }
  673.  
  674.     void FreeInputType() {
  675.        if (m_bInputTypeSet) {
  676.           MoFreeMediaType( &m_InputType );
  677.           m_bInputTypeSet = FALSE;
  678.        }
  679.     }
  680.  
  681.     void FreeOutputType() {
  682.        if (m_bOutputTypeSet) {
  683.           MoFreeMediaType( &m_OutputType );
  684.           m_bOutputTypeSet = FALSE;
  685.        }
  686.     }
  687.  
  688. protected:
  689.     // mediatype stuff
  690.     BOOL m_bInputTypeSet;
  691.     BOOL m_bOutputTypeSet;
  692.     DMO_MEDIA_TYPE m_InputType;
  693.     DMO_MEDIA_TYPE m_OutputType;
  694.  
  695.     BOOL m_bIncomplete;
  696. protected:
  697. #ifdef DMO_NOATL
  698.     CRITICAL_SECTION m_cs;
  699. #else
  700.     CComAutoCriticalSection m_cs;
  701. #endif
  702. };
  703.  
  704.  
  705.  
  706. //
  707. // C1for1DMO - base class for 1-input/1-output DMOs which
  708. //  - work on whole samples at a time, one sample per buffer
  709. //  - produce exactly one output sample for every input sample
  710. //  - don't need to accumulate more than 1 input sample before producing
  711. //  - don't produce any additional stuff at the end
  712. //  - the output sample corresponds in time to the input sample
  713. //
  714. // The derived class must implement:
  715. //    HRESULT Process(BYTE* pIn,
  716. //                    ULONG ulBytesIn,
  717. //                    BYTE* pOut,
  718. //                    ULONG* pulProduced);
  719. //    HRESULT GetSampleSizes(ULONG* pulMaxInputSampleSize,
  720. //                           ULONG* pulMaxOutputSampleSize);
  721. //
  722. //
  723. // The derived class should implement:
  724. //    HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  725. //    HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  726. //    HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt);
  727. //    HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt);
  728. //
  729. // The derived class may implement if it needs to:
  730. //    HRESULT Init();
  731. //
  732. // The following methods are implemented by the base class.  The derived class
  733. // should call these to find out if the input/output type has been set and if
  734. // so what it was set to.
  735. //    DMO_MEDIA_TYPE *InputType();
  736. //    DMO_MEDIA_TYPE *OutputType().
  737. //
  738.  
  739. class C1for1DMO : public C1in1outDMO
  740. {
  741. public:
  742.     C1for1DMO() :
  743.        m_pBuffer(NULL)
  744.     {
  745.     }
  746.     ~C1for1DMO() {
  747.        if (m_pBuffer)
  748.           m_pBuffer->Release();
  749.     }
  750.  
  751. protected:
  752.     //
  753.     // Implement C1in1outDMO overridables
  754.     //
  755.     virtual HRESULT GetInputFlags(DWORD* pdwFlags) {
  756.        *pdwFlags = DMO_INPUT_STREAMF_WHOLE_SAMPLES |
  757.                    DMO_INPUT_STREAMF_SINGLE_SAMPLE_PER_BUFFER;
  758.        return NOERROR;
  759.     }
  760.     virtual HRESULT GetOutputFlags(DWORD* pdwFlags) {
  761.        *pdwFlags = DMO_OUTPUT_STREAMF_WHOLE_SAMPLES |
  762.                    DMO_OUTPUT_STREAMF_SINGLE_SAMPLE_PER_BUFFER;
  763.        return NOERROR;
  764.     }
  765.  
  766.     HRESULT GetInputSizeInfo(ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment) {
  767.        HRESULT hr = GetSampleSizes(&m_ulMaxInputSize, &m_ulMaxOutputSize);
  768.        if (FAILED(hr))
  769.           return hr;
  770.  
  771.        *pulSize = m_ulMaxInputSize;
  772.        *pcbMaxLookahead = 0;
  773.        *pulAlignment = 1;
  774.        return NOERROR;
  775.     }
  776.     HRESULT GetOutputSizeInfo(ULONG *pulSize, ULONG *pulAlignment) {
  777.        HRESULT hr = GetSampleSizes(&m_ulMaxInputSize, &m_ulMaxOutputSize);
  778.        if (FAILED(hr))
  779.           return hr;
  780.  
  781.        *pulSize = m_ulMaxOutputSize;
  782.        *pulAlignment = 1;
  783.        return NOERROR;
  784.     }
  785.     HRESULT PrepareForStreaming() {
  786.        HRESULT hr = GetSampleSizes(&m_ulMaxInputSize, &m_ulMaxOutputSize);
  787.        if (FAILED(hr))
  788.           return hr;
  789.  
  790.        return Init();
  791.     }
  792.     HRESULT AcceptingInput() {
  793.        return m_pBuffer ? S_FALSE : S_OK; // accept unless holding one already
  794.     }
  795.     HRESULT AcceptInput(BYTE* pData,
  796.                         ULONG ulSize,
  797.                         DWORD dwFlags,
  798.                         REFERENCE_TIME rtTimestamp,
  799.                         REFERENCE_TIME rtTimelength,
  800.                         IMediaBuffer* pMediaBuffer
  801.     ) {
  802.        if (AcceptingInput() != S_OK)
  803.           return E_FAIL;
  804.        m_pData        = pData;
  805.        m_ulSize       = ulSize;
  806.        m_dwFlags      = dwFlags;
  807.        m_rtTimestamp  = rtTimestamp;
  808.        m_rtTimelength = rtTimelength;
  809.        m_pBuffer      = pMediaBuffer;
  810.        pMediaBuffer->AddRef();
  811.        return NOERROR;
  812.     }
  813.     HRESULT DoFlush() {
  814.         Discontinuity();
  815.         if (m_pBuffer) {
  816.             m_pBuffer->Release();
  817.             m_pBuffer = NULL;
  818.         }
  819.         return NOERROR;
  820.     }
  821.     HRESULT ProduceOutput(BYTE *pOut,
  822.                           ULONG ulAvail,
  823.                           ULONG* pulUsed,
  824.                           DWORD* pdwStatus,
  825.                           REFERENCE_TIME *prtTimestamp,
  826.                           REFERENCE_TIME *prtTimelength
  827.     ) {
  828.        *pulUsed = 0;
  829.        *pdwStatus = 0;
  830.  
  831.        if (!m_pBuffer)
  832.           return S_FALSE;
  833.  
  834.        if (pOut) {
  835.           if (ulAvail < m_ulMaxOutputSize)
  836.              return E_INVALIDARG;
  837.        }
  838.  
  839.        HRESULT hr = Process(m_pData, m_ulSize, pOut, pulUsed);
  840.  
  841.        m_pBuffer->Release();
  842.        m_pBuffer = NULL;
  843.  
  844.        if (FAILED(hr))
  845.           return hr;
  846.  
  847.        if (*pulUsed == 0)
  848.           return S_FALSE;
  849.  
  850.        if (m_dwFlags & DMO_INPUT_DATA_BUFFERF_SYNCPOINT)
  851.           *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT;
  852.        if (m_dwFlags & DMO_INPUT_DATA_BUFFERF_TIME)
  853.           *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIME;
  854.        if (m_dwFlags & DMO_INPUT_DATA_BUFFERF_TIMELENGTH)
  855.           *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH;
  856.        *prtTimestamp = m_rtTimestamp;
  857.        *prtTimelength = m_rtTimelength;
  858.  
  859.        return hr;
  860.     }
  861. protected:
  862.     //
  863.     // To be implemented by derived class
  864.     //
  865.     virtual HRESULT Process(BYTE* pIn,
  866.                             ULONG ulBytesIn,
  867.                             BYTE* pOut,
  868.                             ULONG* pulProduced) = 0;
  869.     virtual HRESULT GetSampleSizes(ULONG* pulMaxInputSampleSize,
  870.                                    ULONG* pulMaxOutputSampleSize) = 0;
  871.     virtual HRESULT Init() {
  872.        return NOERROR;
  873.     }
  874.  
  875.    IMediaBuffer* m_pBuffer;
  876.    BYTE* m_pData;
  877.    ULONG m_ulSize;
  878.    DWORD m_dwFlags;
  879.    REFERENCE_TIME m_rtTimestamp;
  880.    REFERENCE_TIME m_rtTimelength;
  881.  
  882.    ULONG m_ulMaxOutputSize;
  883.    ULONG m_ulMaxInputSize;
  884. };
  885.  
  886. //
  887. // C1for1QCDMO - adds an IDMOQualityControl implementation to C1for1DMO. Just like
  888. // C1for1DMO, this base class assumes that the DMO produces exactly one output sample
  889. // for each input sample, etc. etc.
  890. //
  891. // A class that derives from C1for1QCDMO has access to / ability to override all
  892. // the same methods as with C1for1DMO, except
  893. //   (1) A class derived from C1for1QCDMO should override QCProcess instead of
  894. //       Process because C1for1QCDMO::Process implements some code required for
  895. //       quality control.  QCProcess has the same prototype as C1for1DMO::Process.
  896. //   (2) If a class derived from C1for1QCDMO overrides Init(), it should at some
  897. //       point call C1for1QCDMO::Init() to make sure C1for1QCDMO's quality control
  898. //       data members are properly initialized.
  899. //
  900. class C1for1QCDMO : public C1for1DMO, public IDMOQualityControl {
  901. public:
  902.    //
  903.    // IDMOQualityControl
  904.    //
  905.    STDMETHODIMP SetNow(REFERENCE_TIME rtNow) {
  906.       // Remember SetNow values even if quality control is not currently enabled
  907.       DWORD dwTicks = GetTickCount();
  908.       CDMOAutoLock l(&m_cs);
  909.       m_rtNow = rtNow;
  910.       m_dwNow = dwTicks;
  911.       return NOERROR;
  912.    }
  913.    STDMETHODIMP SetStatus(DWORD dwFlags) {
  914.       // Any point in grabbing the object lock here ?
  915.       if (dwFlags & DMO_QUALITY_STATUS_ENABLED)
  916.          m_bQualityControlEnabled = TRUE;
  917.       else
  918.          m_bQualityControlEnabled = FALSE;
  919.       return NOERROR;
  920.    }
  921.    STDMETHODIMP GetStatus(DWORD *pdwFlags) {
  922.       // Any point in grabbing the object lock here ?
  923.       if (m_bQualityControlEnabled)
  924.          *pdwFlags = DMO_QUALITY_STATUS_ENABLED;
  925.       else
  926.          *pdwFlags = 0;
  927.       return NOERROR;
  928.    }
  929.  
  930. protected:
  931.    HRESULT Init() {
  932.       m_bQualityControlEnabled = FALSE;
  933.       m_rtProcess = 100000; // 10 ms - initial guess at processing time
  934.       return NOERROR;
  935.    }
  936.  
  937.    // Override Process to add quality control
  938.    HRESULT Process(BYTE* pIn,ULONG ulBytesIn,BYTE* pOut,ULONG* pulProduced) {
  939.       // Skip the sample if it is likely to be late.
  940.       if (m_bQualityControlEnabled &&
  941.           (m_dwFlags & DMO_INPUT_DATA_BUFFERF_TIME) &&   // timestamp present
  942.           (m_rtNow + (GetTickCount() - m_dwNow) * 10000 + m_rtProcess > m_rtTimestamp + 0000000)) {
  943.          *pulProduced = 0;
  944.          return S_FALSE;
  945.       }
  946.  
  947.       DWORD dwBefore = GetTickCount();
  948.       HRESULT hr = QCProcess(m_pData, m_ulSize, pOut, pulProduced);
  949.       DWORD dwAfter = GetTickCount();
  950.  
  951.       // Make the new m_rtProcess a weighted average of the old m_rtProcess
  952.       // and the value we just got.  0.8 and 0.2 give a time constant of about 4,
  953.       // and it takes about 10 iterations to reach 90% - seems reasonable, but
  954.       // I don't know what the optimal value is.
  955.       m_rtProcess = (REFERENCE_TIME)(0.8 * m_rtProcess + 0.2 * (((REFERENCE_TIME)(dwAfter - dwBefore)) * 10000));
  956.       return hr;
  957.    }
  958.  
  959.    // To be implemented by derived class
  960.    virtual HRESULT QCProcess(BYTE* pIn,
  961.                              ULONG ulBytesIn,
  962.                              BYTE* pOut,
  963.                              ULONG* pulProduced) = 0;
  964.  
  965. private:
  966.    // variables used by quality control code
  967.    BOOL m_bQualityControlEnabled;
  968.    REFERENCE_TIME m_rtNow;
  969.    DWORD m_dwNow;
  970.    REFERENCE_TIME m_rtProcess; // average processing delay
  971. };
  972.  
  973. //
  974. // CFBRDMO - DMO base class for 'fixed bitrate' DMOs.  More specifically,
  975. // this base class assumes the following:
  976. //  - 1 input, 1 output;
  977. //  - both input and output consist of equally sized 'quanta';
  978. //  - input/output quantum sizes can be determined from mediatypes;
  979. //  - each output quantum can be generated independently (without looking at
  980. //     previous output quanta);
  981. //  - if multiple input quanta are needed to generate a particular output
  982. //     quantum ('window overhead'), then the range of input required has an upper
  983. //     bound derived from mediatypes on both sides (i.e., both 'lookahead'
  984. //     and 'input memory' are bounded).
  985. //
  986. // The derived class must implement the following virtual functions:
  987. //    HRESULT FBRProcess(DWORD cQuanta, BYTE *pIn, BYTE *pOut);
  988. //    HRESULT GetStreamingParams(
  989. //       DWORD *pdwInputQuantumSize, // in bytes
  990. //       DWORD *pdwOutputQuantumSize, // in bytes
  991. //       DWORD *pdwMaxLookahead, // in input quanta, 0 means no lookahead
  992. //       DWORD *pdwLookBehind,
  993. //       REFERENCE_TIME *prtQuantumDuration, // same for input and output quanta
  994. //       REFERENCE_TIME *prtDurationDenominator // optional, normally 1
  995. //    );
  996. // The derived class should also implement the following:
  997. //    HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  998. //    HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt);
  999. //    HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt);
  1000. //    HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt);
  1001. // The derived class may need to implement the followng:
  1002. //    HRESULT Init();
  1003. //    HRESULT Discontinuity();
  1004. //
  1005. // The derived class may use these entry points into the base class to get
  1006. // the currently set mediatypes:
  1007. //    DMO_MEDIA_TYPE *InputType();
  1008. //    DMO_MEDIA_TYPE *OutputType().
  1009. //
  1010. // The sum of *pdwMaxLookahead and *pdwLoookbehind is the 'window overhead' of
  1011. // the algorithm (the window overhead is 0 if the algorithm only needs the
  1012. // current input sample).
  1013. //
  1014. // Because the non-zero window overhead case is more complicated, it is handled by a
  1015. // separate set of functions in this base class.  The names of all non-zero
  1016. // window overhead functions have the 'NZWO' prefix.  The names of the
  1017. // zero window overhead functions begin with 'ZWO'.
  1018. //
  1019. // A data copy on the input side is necessary in the non-zero window overhead case.
  1020. //
  1021.  
  1022. class CFBRDMO : public C1in1outDMO
  1023. {
  1024. public:
  1025.     CFBRDMO() :
  1026.        m_bParametersSet(FALSE),
  1027.        m_pMediaBuffer(NULL),
  1028.        m_pAllocAddr(NULL),
  1029.        m_bStreaming(FALSE)
  1030.     {
  1031.     }
  1032.     ~CFBRDMO() {
  1033.        /*
  1034.        if (m_bStreaming)
  1035.           StopStreaming();
  1036.        */
  1037.        if (m_pAllocAddr)
  1038.           delete[] m_pAllocAddr;
  1039.        if (m_pMediaBuffer)
  1040.           m_pMediaBuffer->Release();
  1041.     }
  1042.  
  1043. protected:
  1044.     //
  1045.     // Implement C1in1outDMO overridables
  1046.     //
  1047.     HRESULT GetInputSizeInfo(ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment) {
  1048.        if (!(InputType() && OutputType()))
  1049.           return DMO_E_TYPE_NOT_SET;
  1050.        //
  1051.        // For efficiency reasons we might like to be fed fairly large amounts
  1052.        // of data at a time, but technically all we need is one quantum.
  1053.        //
  1054.        *pulSize = m_ulInputQuantumSize;
  1055.        *pcbMaxLookahead = 0; // this base class does not rely on HOLDS_BUFFERS
  1056.        *pulAlignment = 1;
  1057.        return NOERROR;
  1058.     }
  1059.     HRESULT GetOutputSizeInfo(ULONG *pulSize, ULONG *pulAlignment) {
  1060.        if (!(InputType() && OutputType()))
  1061.           return DMO_E_TYPE_NOT_SET;
  1062.        *pulSize = m_ulOutputQuantumSize;
  1063.        *pulAlignment = 1;
  1064.        return NOERROR;
  1065.     }
  1066.  
  1067.     virtual HRESULT Discontinuity() {
  1068.        m_bDiscontinuity = TRUE;
  1069.        return NOERROR;
  1070.     }
  1071.  
  1072.     virtual HRESULT AcceptInput(BYTE* pData,
  1073.                                 ULONG ulSize,
  1074.                                 DWORD dwFlags,
  1075.                                 REFERENCE_TIME rtTimestamp,
  1076.                                 REFERENCE_TIME rtTimelength,
  1077.                                 IMediaBuffer* pBuffer
  1078.     ) {
  1079.        BOOL bTimestamp = (dwFlags & DMO_INPUT_DATA_BUFFERF_TIME) ? TRUE : FALSE;
  1080.  
  1081.        if (m_ulWindowOverhead)
  1082.           return NZWOProcessInput(pBuffer, pData, ulSize, bTimestamp, rtTimestamp);
  1083.        else
  1084.           return ZWOProcessInput(pBuffer, pData, ulSize, bTimestamp, rtTimestamp);
  1085.     }
  1086.     virtual HRESULT ProduceOutput(BYTE *pOut,
  1087.                                   ULONG ulAvail,
  1088.                                   ULONG* pulUsed,
  1089.                                   DWORD* pdwStatus,
  1090.                                   REFERENCE_TIME *prtTimestamp,
  1091.                                   REFERENCE_TIME *prtTimelength
  1092.     ) {
  1093.        HRESULT hr;
  1094.        if (!m_bParametersSet)
  1095.           return DMO_E_TYPE_NOT_SET;
  1096.  
  1097.        // call Discontinuity() if this is the first ProcessOutput() call
  1098.        if (!m_bStreaming) {
  1099.           HRESULT hr = Discontinuity();
  1100.           if (FAILED(hr))
  1101.              return hr;
  1102.           m_bStreaming = TRUE;
  1103.        }
  1104.  
  1105.        *pdwStatus = 0;
  1106.  
  1107.        ULONG ulInputQuantaAvailable = InputQuantaAvailable();
  1108.        if (!ulInputQuantaAvailable)
  1109.           return S_FALSE; // did not produce anything
  1110.  
  1111.        ULONG ulOutputQuantaPossible = ulAvail / m_ulOutputQuantumSize;
  1112.        if (!ulOutputQuantaPossible)
  1113.           return E_INVALIDARG;
  1114.  
  1115.        ULONG ulQuantaToProcess = min(ulOutputQuantaPossible, ulInputQuantaAvailable);
  1116.        assert(ulQuantaToProcess > 0);
  1117.  
  1118.        BOOL bTimestamp=0;
  1119.        if (m_ulWindowOverhead)
  1120.           hr = NZWOProcessOutput(pOut, ulQuantaToProcess, &bTimestamp, prtTimestamp);
  1121.        else
  1122.           hr = ZWOProcessOutput(pOut, ulQuantaToProcess, &bTimestamp, prtTimestamp);
  1123.        if (FAILED(hr))
  1124.           return hr;
  1125.  
  1126.        *pulUsed = ulQuantaToProcess * m_ulOutputQuantumSize;
  1127.        *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT;
  1128.  
  1129.        if (bTimestamp)
  1130.           *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_TIME;
  1131.  
  1132.        // any data left ?
  1133.        if (InputQuantaAvailable()) // yes - set incomplete
  1134.           *pdwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
  1135.        else if (m_bDiscontinuity) // no - process any discontinuity
  1136.           DoFlush();
  1137.  
  1138.        return NOERROR;
  1139.     }
  1140.     HRESULT DoFlush()
  1141.     {
  1142.        Discontinuity();
  1143.  
  1144.        // reset flags
  1145.        m_bDiscontinuity = FALSE;
  1146.        m_bTimestamps = FALSE;
  1147.  
  1148.        if (m_ulWindowOverhead)
  1149.           NZWODiscardData();
  1150.        else
  1151.           ZWODiscardData();
  1152.  
  1153.        return NOERROR;
  1154.     }
  1155.     HRESULT AcceptingInput() {
  1156.        if (!m_bParametersSet) // uninitialized
  1157.           return S_FALSE;
  1158.  
  1159.        BOOL bResult;
  1160.        if (m_ulWindowOverhead)
  1161.           bResult = NZWOQueryAccept();
  1162.        else
  1163.           bResult = ZWOQueryAccept();
  1164.  
  1165.        return bResult ? S_OK : S_FALSE;
  1166.     }
  1167.     // End C1in1out overridables implementation
  1168.  
  1169. private:
  1170.     //
  1171.     // Common private code (window overhead or no window overhead)
  1172.     //
  1173.     // returns the number of input quanta available minus any window overhead
  1174.     ULONG InputQuantaAvailable() {
  1175.        if (m_ulWindowOverhead)
  1176.           return NZWOAvail();
  1177.        else
  1178.           return ZWOAvail();
  1179.     }
  1180.  
  1181.     // Private method to compute/allocate stuff once all types have been set.
  1182.     HRESULT PrepareForStreaming () {
  1183.        m_bParametersSet = FALSE;
  1184.        // Now that both types are set, query the derived class for params
  1185.        HRESULT hr;
  1186.        if (FAILED(hr = GetStreamingParams(&m_ulInputQuantumSize,
  1187.                                &m_ulOutputQuantumSize,
  1188.                                &m_ulLookahead,
  1189.                                &m_ulLookbehind,
  1190.                                &m_rtDurationNumerator,
  1191.                                &m_rtDenominator)))
  1192.           return hr;
  1193.  
  1194.        // m_ulOutputQuantumSize and m_ulInputQuantumSize should never be 0.
  1195.        assert( (0 != m_ulInputQuantumSize) && (0 != m_ulOutputQuantumSize) );
  1196.  
  1197.        if (!m_rtDenominator) {
  1198.           assert(!"bad object - duration denominator should not be 0 !");
  1199.           return E_FAIL;
  1200.        }
  1201.        // Attempt to reduce the fraction.  Probably the most complicated number
  1202.        // we will ever see is 44100 = (3 * 7 * 2 * 5) ^ 2, so trying the first
  1203.        // few numbers should suffice in most cases.
  1204.        DWORD dwP[] = {2,3,5,7,11,13,17,19,23,29,31};
  1205.        for (DWORD c = 0; c < sizeof(dwP) / sizeof(DWORD); c++) {
  1206.           while ((m_rtDurationNumerator % dwP[c] == 0) &&
  1207.                  (m_rtDenominator % dwP[c] == 0)) {
  1208.              m_rtDurationNumerator /= dwP[c];
  1209.              m_rtDenominator /= dwP[c];
  1210.           }
  1211.        }
  1212.  
  1213.        // We cannot afford to have huge denominators, unfortunately, because
  1214.        // we store timestamp numerators using 64 bits, so a large denominator
  1215.        // could result in timestamp overflows.  So if the denominator is still
  1216.        // too large, reduce it anyway with loss of precision.
  1217.        ULONG ulMax = 0x10000; // largest acceptable denominator value
  1218.        if (m_rtDenominator >= ulMax) {
  1219.           double actual_ratio = (double)m_rtDurationNumerator * (double)m_rtDenominator;
  1220.           ULONG ulDenominator = 1;
  1221.           // Repeatedly increase the denominator until either the actual ratio
  1222.           // can be represented precisely using the denominator, or the
  1223.           // denominator gets too large.
  1224.           do {
  1225.              double fractional_part = actual_ratio * (double)ulDenominator
  1226.                                     - floor(actual_ratio * (double)ulDenominator);
  1227.              if (fractional_part == 0)
  1228.                 break;
  1229.              ULONG ulNewDenominator = (ULONG)floor(ulDenominator / fractional_part);
  1230.              if (ulNewDenominator >= ulMax)
  1231.                 break;
  1232.              ulDenominator = ulNewDenominator;
  1233.           } while(1);
  1234.           m_rtDurationNumerator = (ULONG)floor(actual_ratio * ulDenominator);
  1235.           m_rtDenominator = ulDenominator;
  1236.        }
  1237.  
  1238.        m_ulWindowOverhead = m_ulLookahead + m_ulLookbehind;
  1239.        if (!m_ulWindowOverhead) // No window overhead - the simple case
  1240.           m_bParametersSet = TRUE;
  1241.        else // The complicated case with window overhead
  1242.           AllocateCircularBuffer();
  1243.  
  1244.        m_bTimestamps = FALSE;
  1245.        m_bDiscontinuity = FALSE;
  1246.  
  1247.        if (m_bStreaming) {
  1248.           //StopStreaming();
  1249.           m_bStreaming = FALSE;
  1250.        }
  1251.        
  1252.        hr = Init();
  1253.        if( FAILED( hr ) ) {
  1254.           m_bParametersSet = FALSE;
  1255.           return hr;
  1256.        }
  1257.  
  1258.        return m_bParametersSet ? NOERROR : E_FAIL;
  1259.     }
  1260.     // end common code
  1261.  
  1262.     //
  1263.     // zero window overhead case code
  1264.     //
  1265.     HRESULT ZWOProcessInput(IMediaBuffer* pBuffer,
  1266.                                      BYTE* pData,
  1267.                                      ULONG ulSize,
  1268.                                      BOOL bTimestamp,
  1269.                                      REFERENCE_TIME rtTimestamp) {
  1270.        assert(!m_pMediaBuffer);
  1271.  
  1272.        m_bTimestamp = bTimestamp;
  1273.        m_rtTimestamp = rtTimestamp;
  1274.        m_pData = pData;
  1275.        m_ulData = ulSize;
  1276.        m_ulUsed = 0;
  1277.  
  1278.        // make sure they gave us a meaningful amount of data
  1279.        if (m_ulData < m_ulInputQuantumSize)
  1280.           return S_FALSE;
  1281.  
  1282.        // save the buffer we were given
  1283.        m_pMediaBuffer = pBuffer;
  1284.        pBuffer->AddRef();
  1285.        return NOERROR;
  1286.     }
  1287.     HRESULT ZWOProcessOutput(BYTE* pOut,
  1288.                                       ULONG ulQuantaToProcess,
  1289.                                       BOOL* pbTimestamp,
  1290.                                       REFERENCE_TIME* prtTimestamp) {
  1291.        assert(m_ulUsed % m_ulInputQuantumSize == 0);
  1292.        HRESULT hr = FBRProcess(ulQuantaToProcess, m_pData + m_ulUsed, pOut);
  1293.        if (FAILED(hr)) return hr;
  1294.        ZWOConsume(ulQuantaToProcess);
  1295.  
  1296.        if (m_bTimestamp) { // there was a timestamp on this input buffer
  1297.           // m_rtTimestamp refers to the beginning of the input buffer.
  1298.           // Extrapolate to the beginning of the area we just processed.
  1299.           *prtTimestamp = m_rtTimestamp +
  1300.                (m_ulUsed % m_ulInputQuantumSize) * m_rtDurationNumerator /
  1301.                                                    m_rtDenominator;
  1302.           *pbTimestamp = TRUE;
  1303.        }
  1304.        else if (m_bTimestamps) { // there was a timestamp earlier
  1305.           // should we extrapolate from a previous timestamp ?
  1306.           *pbTimestamp = FALSE;
  1307.        }
  1308.        else // no timestamps at all
  1309.           *pbTimestamp = FALSE;
  1310.  
  1311.        return NOERROR;
  1312.     }
  1313.     ULONG ZWOAvail() {
  1314.        if (m_pMediaBuffer) {
  1315.           assert(m_ulData - m_ulUsed >= m_ulInputQuantumSize);
  1316.           return (m_ulData - m_ulUsed) / m_ulInputQuantumSize;
  1317.        }
  1318.        else
  1319.           return 0;
  1320.     }
  1321.     void ZWOConsume(ULONG ulN) { // the zero window overhead version
  1322.        assert(m_pMediaBuffer);
  1323.        m_ulUsed += ulN * m_ulInputQuantumSize;
  1324.        assert(m_ulData >= m_ulUsed);
  1325.        if (m_ulData - m_ulUsed < m_ulInputQuantumSize) {
  1326.           m_pMediaBuffer->Release();
  1327.           m_pMediaBuffer = NULL;
  1328.        }
  1329.     }
  1330.     BOOL ZWOQueryAccept() {
  1331.         // Accept if and only if (IFF) the DMO is not already holding a buffer.
  1332.        if (!m_pMediaBuffer)
  1333.           return TRUE;
  1334.        else
  1335.           return FALSE;
  1336.     }
  1337.     void ZWODiscardData() {
  1338.        if (m_pMediaBuffer) {
  1339.           m_pMediaBuffer->Release();
  1340.           m_pMediaBuffer = NULL;
  1341.        }
  1342.     }
  1343.     // End zero window overhead case code
  1344.  
  1345.     //
  1346.     // Non zero window overhead case code.
  1347.     //
  1348.     HRESULT NZWOProcessInput(IMediaBuffer* pBuffer,
  1349.                                   BYTE* pData,
  1350.                                   ULONG ulSize,
  1351.                                   BOOL bTimestamp,
  1352.                                   REFERENCE_TIME rtTimestamp) {
  1353.        if (bTimestamp) { // process the timestamp
  1354.           if (!m_bTimestamps) { // this is the first timestamp we've seen
  1355.              // Just getting started - initialize the timestamp to refer to
  1356.              // the first input quantum for which we will actually generate
  1357.              // output (the first m_ulLookbehind quanta are pure lookbehind and
  1358.              // generate no output).
  1359.              m_rtTimestampNumerator = rtTimestamp * m_rtDenominator
  1360.                                     + m_ulLookbehind * m_rtDurationNumerator;
  1361.  
  1362.           }
  1363.           else {
  1364.              // We are already streaming and just got a new timestamp.  Use it
  1365.              // to check if our stored timestamp has somehow drifted away from
  1366.              // where it should be and adjust if it is far enough off.
  1367.  
  1368.              ULONG ulInputQuantaAvailable = InputQuantaAvailable();
  1369.              if (ulInputQuantaAvailable) {
  1370.                 // ulInputQuantaAvailable is how far back in time the next
  1371.                 // quantum we would process is located relative the beginning
  1372.                 // of the new buffer we just received.
  1373.  
  1374.                 // Compute what the timestamp back there ought to be now.
  1375.                 REFERENCE_TIME rtTimestampNumerator;
  1376.                 rtTimestampNumerator = m_rtDenominator * rtTimestamp
  1377.                                      - ulInputQuantaAvailable * m_rtDurationNumerator;
  1378.  
  1379.                 // Adjust the stored timestamp if it is off by more than half
  1380.                 // the duration of a quantum.  Should also have a DbgLog here.
  1381.                 if ((m_rtTimestampNumerator >= rtTimestampNumerator + m_rtDurationNumerator / 2) ||
  1382.                     (m_rtTimestampNumerator <= rtTimestampNumerator - m_rtDurationNumerator / 2)) {
  1383.                    m_rtTimestampNumerator = rtTimestampNumerator;
  1384.                 }
  1385.              }
  1386.              else {
  1387.                 // We must still be accumulating the initial window overhead.
  1388.                 // Too early to need an adjustment, one would hope.
  1389.              }
  1390.           }
  1391.           m_bTimestamps = TRUE;
  1392.        }
  1393.  
  1394.        if (BufferUsed() + ulSize > m_ulBufferAllocated)
  1395.           return E_FAIL; // need a max input size to prevent this
  1396.  
  1397.        // append to our buffer
  1398.        AppendData(pData, ulSize);
  1399.  
  1400.        // are we ready to produce now ?
  1401.        if (NZWOAvail())
  1402.           return NOERROR;
  1403.        else
  1404.           return S_FALSE; // no output can be produced yet
  1405.     }
  1406.     HRESULT NZWOProcessOutput(BYTE* pOut,
  1407.                                    ULONG ulQuantaToProcess,
  1408.                                    BOOL* pbTimestamp,
  1409.                                    REFERENCE_TIME* prtTimestamp) {
  1410.        //
  1411.        // Handle any timestamps
  1412.        //
  1413.        if (m_bTimestamps) {
  1414.           // In window overhead mode the stored timestamp refers to the input
  1415.           // data immediately after lookbehind, which corresponds to the
  1416.           // begining of the output buffer by definition of FDRProcess.
  1417.           *prtTimestamp = m_rtTimestampNumerator / m_rtDenominator;
  1418.           *pbTimestamp = TRUE;
  1419.  
  1420.        }
  1421.        else
  1422.           *pbTimestamp = FALSE;
  1423.  
  1424.        //
  1425.        // Handle the data
  1426.        //
  1427.        HRESULT hr;
  1428.        ULONG ulInputNeeded = m_ulInputQuantumSize * (ulQuantaToProcess + m_ulWindowOverhead);
  1429.        assert(ulInputNeeded < BufferUsed());
  1430.        if (m_ulDataHead + ulInputNeeded <= m_ulBufferAllocated) {
  1431.           // No wraparound, everything is easy
  1432.           hr = FBRProcess(ulQuantaToProcess,
  1433.                           m_pCircularBuffer + m_ulDataHead + m_ulLookbehind * m_ulInputQuantumSize,
  1434.                           pOut);
  1435.           if (FAILED(hr))
  1436.              return hr;
  1437.           NZWOConsume(ulQuantaToProcess);
  1438.        }
  1439.        else { // The data we want to send wraps around the end
  1440.           // Q.: does it wrap around inside the window overhead area
  1441.           // or inside the main data area ?
  1442.           if (m_ulDataHead + m_ulWindowOverhead * m_ulInputQuantumSize < m_ulBufferAllocated) {
  1443.              // The wraparound occurs inside the main data area.  Advance the
  1444.              // window overhead up to the wraparound point by processing some data.
  1445.              ULONG ulAdvance = m_ulBufferAllocated - (m_ulDataHead + m_ulWindowOverhead * m_ulInputQuantumSize);
  1446.              assert(ulAdvance % m_ulInputQuantumSize == 0);
  1447.              ulAdvance /= m_ulInputQuantumSize; // convert to quanta
  1448.              assert(ulAdvance > 0);
  1449.              assert(ulAdvance < ulQuantaToProcess);
  1450.              hr = FBRProcess(ulAdvance,
  1451.                              m_pCircularBuffer + m_ulDataHead + m_ulLookbehind * m_ulInputQuantumSize,
  1452.                              pOut);
  1453.              if (FAILED(hr))
  1454.                 return hr;
  1455.              NZWOConsume(ulAdvance);
  1456.  
  1457.              // Adjust stuff so that the code below can act
  1458.              // as if this extra process call never happened.
  1459.              pOut += m_ulOutputQuantumSize * ulAdvance;
  1460.              ulQuantaToProcess -= ulAdvance;
  1461.              assert(ulQuantaToProcess > 0);
  1462.  
  1463.              // Now the wraparound point should be exactly on the boundary
  1464.              // between window overhead and main data.
  1465.              assert(m_ulDataHead + m_ulWindowOverhead * m_ulInputQuantumSize == m_ulBufferAllocated);
  1466.           } // wraparound in main data
  1467.  
  1468.           // When we get here, the wraparound point occurs somewhere inside
  1469.           // the window overhead area or right on the border between window overhead and
  1470.           // main data.
  1471.           assert(m_ulDataHead + m_ulWindowOverhead * m_ulInputQuantumSize >= m_ulBufferAllocated);
  1472.           ULONG ulLookaheadToCopy = m_ulBufferAllocated - m_ulDataHead;
  1473.  
  1474.           // copy to the special area we reserved at the front
  1475.           memcpy(m_pCircularBuffer - ulLookaheadToCopy,
  1476.                  m_pCircularBuffer + m_ulDataHead,
  1477.                  ulLookaheadToCopy);
  1478.  
  1479.           // Now the block we are interested in is all in one piece
  1480.           hr = FBRProcess(ulQuantaToProcess,
  1481.                           m_pCircularBuffer - ulLookaheadToCopy  + m_ulLookbehind * m_ulInputQuantumSize,
  1482.                           pOut);
  1483.           if (FAILED(hr))
  1484.              return hr;
  1485.           NZWOConsume(ulQuantaToProcess);
  1486.        } // data handling - wraparound case
  1487.        return NOERROR;
  1488.     }
  1489.     void AllocateCircularBuffer() {
  1490.        // free any previously allocated input buffer
  1491.        if (m_pAllocAddr)
  1492.           delete[] m_pAllocAddr;
  1493.  
  1494.        // need a better way to decide this number
  1495.        m_ulBufferAllocated = max(m_ulInputQuantumSize * 16, 65536L);
  1496.        m_ulDataHead = m_ulDataTail = 0;
  1497.  
  1498.        // reserve room at the front for copying window overhead
  1499.        ULONG ulPrefix = m_ulWindowOverhead * m_ulInputQuantumSize;
  1500.        m_pAllocAddr = new BYTE[m_ulBufferAllocated + ulPrefix];
  1501.        if (!m_pAllocAddr)
  1502.           return;
  1503.        m_pCircularBuffer = m_pAllocAddr + ulPrefix;
  1504.  
  1505.        m_bParametersSet = TRUE;
  1506.     }
  1507.     BOOL NZWOQueryAccept() {
  1508.        // We are using a temp input buffer.  Is there room to append more ?
  1509.        // The answer really depends on how much data they will try to feed
  1510.        // us.  Without knowing the maximum input buffer size, we will accept
  1511.        // more if the input buffer is less than half full.
  1512.        if (2 * BufferUsed() < m_ulBufferAllocated)
  1513.           return TRUE;
  1514.        else
  1515.           return FALSE;
  1516.     }
  1517.     ULONG NZWOAvail() {
  1518.        ULONG ulInputQuantaAvailable = BufferUsed() / m_ulInputQuantumSize;
  1519.        if (ulInputQuantaAvailable > m_ulWindowOverhead)
  1520.           return ulInputQuantaAvailable - m_ulWindowOverhead;
  1521.        else
  1522.           return 0;
  1523.     }
  1524.     void NZWOConsume(ULONG ulN) { // the window overhead version
  1525.        assert(ulN * m_ulInputQuantumSize <= BufferUsed());
  1526.        m_ulDataHead += ulN * m_ulInputQuantumSize;
  1527.        if (m_ulDataHead > m_ulBufferAllocated) //wraparound
  1528.           m_ulDataHead -= m_ulBufferAllocated;
  1529.  
  1530.        // Advance the timestamp.
  1531.        // The same denominator is used for both timestamp and duration.
  1532.        m_rtTimestampNumerator += ulN * m_rtDurationNumerator;
  1533.     }
  1534.     ULONG BufferUsed() {
  1535.        if (m_ulDataTail >= m_ulDataHead)
  1536.           return m_ulDataTail - m_ulDataHead;
  1537.        else
  1538.           return m_ulBufferAllocated - (m_ulDataHead - m_ulDataTail);
  1539.     }
  1540.     void AppendData(BYTE *pData, ULONG ulSize) {
  1541.        if (m_ulDataTail + ulSize <= m_ulBufferAllocated) { // no wraparound
  1542.           memcpy(m_pCircularBuffer + m_ulDataTail, pData, ulSize);
  1543.           m_ulDataTail += ulSize;
  1544.        }
  1545.        else { // wraparound
  1546.           memcpy(m_pCircularBuffer + m_ulDataTail, pData, m_ulBufferAllocated - m_ulDataTail);
  1547.           memcpy(m_pCircularBuffer, pData + m_ulBufferAllocated - m_ulDataTail, ulSize - (m_ulBufferAllocated - m_ulDataTail));
  1548.           m_ulDataTail += ulSize;
  1549.           m_ulDataTail -= m_ulBufferAllocated;
  1550.        }
  1551.     }
  1552.     void NZWODiscardData() {
  1553.        m_ulDataHead = m_ulDataTail = 0;
  1554.     }
  1555.     // End window overhead case code
  1556.  
  1557.  
  1558. protected:
  1559.     //
  1560.     // To be implemebted by the derived class
  1561.     //
  1562.     virtual HRESULT FBRProcess(DWORD cQuanta, BYTE *pIn, BYTE *pOut) = 0;
  1563.     virtual HRESULT GetStreamingParams(
  1564.                        DWORD *pdwInputQuantumSize, // in bytes
  1565.                        DWORD *pdwOutputQuantumSize, // in bytes
  1566.                        DWORD *pdwMaxLookahead, // in input quanta, 0 means no lookahead
  1567.                        DWORD *pdwLookbehind,
  1568.                        REFERENCE_TIME *prtQuantumDuration, // same for input and output quanta
  1569.                        REFERENCE_TIME *prtDurationDenominator // optional, normally 1
  1570.                     ) = 0;
  1571.     virtual HRESULT Init() {
  1572.        return NOERROR;
  1573.     }
  1574.  
  1575. private:
  1576.  
  1577.     BOOL m_bNewInput;
  1578.  
  1579.     // streaming parameters
  1580.     BOOL m_bParametersSet;
  1581.     ULONG m_ulInputQuantumSize;
  1582.     ULONG m_ulOutputQuantumSize;
  1583.     ULONG m_ulLookahead;
  1584.     ULONG m_ulLookbehind;
  1585.     ULONG m_ulWindowOverhead;
  1586.     REFERENCE_TIME m_rtDurationNumerator;
  1587.     REFERENCE_TIME m_rtDenominator;
  1588.  
  1589.     // streaming state
  1590.     BOOL m_bTimestamps; // we have seen at least one timestamp
  1591.     BOOL m_bDiscontinuity;
  1592.     BOOL m_bStreaming;
  1593.  
  1594.     // zero window overhead case input data
  1595.     IMediaBuffer *m_pMediaBuffer;
  1596.     BYTE *m_pData;
  1597.     ULONG m_ulData;
  1598.     ULONG m_ulUsed;
  1599.     BOOL m_bTimestamp; // timestamp on current buffer
  1600.     REFERENCE_TIME m_rtTimestamp;
  1601.  
  1602.     // window overhead case input data
  1603.     BYTE *m_pCircularBuffer;
  1604.     BYTE *m_pAllocAddr;
  1605.     ULONG m_ulBufferAllocated;
  1606.     ULONG m_ulDataHead;
  1607.     ULONG m_ulDataTail;
  1608.     REFERENCE_TIME m_rtTimestampNumerator; // uses the same denominator as duration
  1609.  
  1610. };
  1611.  
  1612.  
  1613. // CPCMDMO - base class for PCM audio transform filters.
  1614. // Helps non-converting PCM audio transforms with mediatype negotiation.
  1615. // Based on CFBRDMO - study that first.
  1616. //
  1617. // Derived class must implement:
  1618. //     FBRProcess()
  1619. // Deriver class may implement:
  1620. //   Discontinuity() // default implementaion does nothing
  1621. //   Init()          // default implementaion does nothing
  1622. //   GetPCMParams()    // default implementation proposes 44100/2/16
  1623. //   CheckPCMParams()  // default implementation accepts any 8/16 bit format
  1624. //   GetWindowParams()   // default implementation assumes no lookahead/lookbehind
  1625. //
  1626. // This class conveniently provides the following data members accessible
  1627. // by the derived class:
  1628. //   ULONG m_ulSamplingRate
  1629. //   ULONG m_cChannels
  1630. //   BOOL m_b8bit
  1631. //
  1632. #include <mmreg.h>
  1633. #include <uuids.h>
  1634.  
  1635. class CPCMDMO : public CFBRDMO
  1636. {
  1637. protected:
  1638.    //
  1639.    // implement pure virtual CFBRDMO methods
  1640.    //
  1641.    HRESULT GetInputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  1642.       if (ulTypeIndex > 0)
  1643.          return DMO_E_NO_MORE_ITEMS;
  1644.       if (pmt != NULL) {
  1645.          HRESULT hr = GetType(pmt, OutputType());
  1646.          if (FAILED(hr)) {
  1647.             return hr;
  1648.          }
  1649.       }
  1650.  
  1651.       return S_OK;
  1652.    }
  1653.    HRESULT GetOutputType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  1654.       if (ulTypeIndex > 0)
  1655.          return DMO_E_NO_MORE_ITEMS;
  1656.       if (pmt != NULL) {
  1657.          HRESULT hr = GetType(pmt, InputType());
  1658.          if (FAILED(hr)) {
  1659.             return hr;
  1660.          }
  1661.       }
  1662.  
  1663.       return S_OK;
  1664.    }
  1665.    HRESULT CheckInputType(const DMO_MEDIA_TYPE *pmt) {
  1666.       return CheckType(pmt, OutputType());
  1667.    }
  1668.    HRESULT CheckOutputType(const DMO_MEDIA_TYPE *pmt) {
  1669.       return CheckType(pmt, InputType());
  1670.    }
  1671.    HRESULT Init() {
  1672.       return NOERROR;
  1673.    }
  1674.    HRESULT Discontinuity() {
  1675.       return NOERROR;
  1676.    }
  1677.    HRESULT GetStreamingParams(
  1678.               DWORD *pdwInputQuantumSize, // in bytes
  1679.               DWORD *pdwOutputQuantumSize, // in bytes
  1680.               DWORD *pdwMaxLookahead, // in input quanta, 0 means no lookahead
  1681.               DWORD *pdwMaxLookbehind,
  1682.               REFERENCE_TIME *prtQuantumDuration, // same for input and output quanta
  1683.               REFERENCE_TIME *prtDurationDenominator // optional, normally 1
  1684.            ) {
  1685.       // Sanity check: all of this should have been taken care of by base class
  1686.       DMO_MEDIA_TYPE* pmtIn =  InputType();
  1687.       DMO_MEDIA_TYPE* pmtOut = OutputType();
  1688.       if (!pmtIn || !pmtOut)
  1689.          return DMO_E_TYPE_NOT_SET;
  1690.       if (CheckType(pmtIn, NULL) || CheckType(pmtOut, pmtIn))
  1691.          return DMO_E_TYPE_NOT_ACCEPTED;
  1692.  
  1693.       WAVEFORMATEX *pWave = (WAVEFORMATEX*)pmtIn->pbFormat;
  1694.  
  1695.       m_b8bit          = (pWave->wBitsPerSample == 8);
  1696.       m_cChannels      = pWave->nChannels;
  1697.       m_ulSamplingRate = pWave->nSamplesPerSec;
  1698.  
  1699.       *pdwInputQuantumSize    = pWave->nBlockAlign;
  1700.       *pdwOutputQuantumSize   = pWave->nBlockAlign;
  1701.       *prtQuantumDuration     = 10000000; // rt units per sec
  1702.       *prtDurationDenominator = pWave->nSamplesPerSec;
  1703.  
  1704.       GetWindowParams(pdwMaxLookahead, pdwMaxLookbehind);
  1705.       return NOERROR;
  1706.    }
  1707.  
  1708. protected:
  1709.    //
  1710.    // Methods to be overridden by derived class
  1711.    //
  1712.    // We use this to get lookahead/lookbehind from the derived class
  1713.    virtual void GetWindowParams(DWORD *pdwMaxLookahead,
  1714.                                 DWORD *pdwMaxLookbehind) {
  1715.       *pdwMaxLookahead = 0;
  1716.       *pdwMaxLookbehind = 0;
  1717.    }
  1718.    // derived class can override these if it has specific requirements
  1719.    virtual void GetPCMParams(BOOL* pb8bit, DWORD* pcChannels, DWORD* pdwSamplesPerSec) {
  1720.       // These values are what the DMO will advertise in its media type.
  1721.       // Specifying them here does not mean that this is the only acceptable
  1722.       // combination - CheckPCMParams() is the ultimate authority on what we will
  1723.       // accept.
  1724.       *pb8bit = FALSE;
  1725.       *pcChannels = 2;
  1726.       *pdwSamplesPerSec = 44100;
  1727.    }
  1728.    virtual BOOL CheckPCMParams(BOOL b8bit, DWORD cChannels, DWORD dwSamplesPerSec) {
  1729.       // Default implementation accepts anything.  Override if you have specific
  1730.       // requirements WRT sampling rate, number of channels, or bit depth.
  1731.       return TRUE;
  1732.    }
  1733.  
  1734. private:
  1735.    //
  1736.    // private helpers
  1737.    //
  1738.    HRESULT GetType(DMO_MEDIA_TYPE* pmt, const DMO_MEDIA_TYPE *pmtOther) {
  1739.  
  1740.       HRESULT hr;
  1741.  
  1742.       // If the other type is set, enumerate that.  Otherwise propose 44100/2/16.
  1743.       if (pmtOther) {
  1744.          hr = MoCopyMediaType(pmt, pmtOther);
  1745.          if (FAILED(hr)) {
  1746.             return hr;
  1747.          }
  1748.          return NOERROR;
  1749.       }
  1750.  
  1751.       hr = MoInitMediaType(pmt, sizeof(WAVEFORMATEX));
  1752.       if (FAILED(hr))
  1753.          return hr;
  1754.  
  1755.       pmt->majortype  = MEDIATYPE_Audio;
  1756.       pmt->subtype    = MEDIASUBTYPE_PCM;
  1757.       pmt->formattype = FORMAT_WaveFormatEx;
  1758.  
  1759.       WAVEFORMATEX* pWave = (WAVEFORMATEX*) pmt->pbFormat;
  1760.       pWave->wFormatTag = WAVE_FORMAT_PCM;
  1761.  
  1762.       BOOL b8bit;
  1763.       DWORD cChannels;
  1764.       GetPCMParams(&b8bit, &cChannels, &(pWave->nSamplesPerSec));
  1765.       pWave->nChannels = (unsigned short)cChannels;
  1766.       pWave->wBitsPerSample = (WORD) (b8bit ? 8 : 16);
  1767.       pWave->nBlockAlign = (WORD) (pWave->nChannels * pWave->wBitsPerSample / 8);
  1768.       pWave->nAvgBytesPerSec = pWave->nSamplesPerSec * pWave->nBlockAlign;
  1769.       pWave->cbSize = 0;
  1770.  
  1771.       return NOERROR;
  1772.    }
  1773.    HRESULT CheckType(const DMO_MEDIA_TYPE *pmt, DMO_MEDIA_TYPE *pmtOther) {
  1774.  
  1775.       if (NULL == pmt) {
  1776.          return E_POINTER;
  1777.       }
  1778.  
  1779.       // verify that this is PCM with a WAVEFORMATEX format specifier
  1780.       if ((pmt->majortype  != MEDIATYPE_Audio) ||
  1781.           (pmt->subtype    != MEDIASUBTYPE_PCM) ||
  1782.           (pmt->formattype != FORMAT_WaveFormatEx) ||
  1783.           (pmt->cbFormat < sizeof(WAVEFORMATEX)) ||
  1784.           (pmt->pbFormat == NULL))
  1785.          return DMO_E_TYPE_NOT_ACCEPTED;
  1786.  
  1787.       // If other type set, accept only if identical to that.  Otherwise accept
  1788.       // any standard PCM audio.
  1789.       if (pmtOther) {
  1790.          if (memcmp(pmt->pbFormat, pmtOther->pbFormat, sizeof(WAVEFORMATEX)))
  1791.             return DMO_E_TYPE_NOT_ACCEPTED;
  1792.       }
  1793.       else {
  1794.          WAVEFORMATEX* pWave = (WAVEFORMATEX*)pmt->pbFormat;
  1795.          if ((pWave->wFormatTag != WAVE_FORMAT_PCM) ||
  1796.              ((pWave->wBitsPerSample != 8) && (pWave->wBitsPerSample != 16)) ||
  1797.              (pWave->nBlockAlign != pWave->nChannels * pWave->wBitsPerSample / 8) ||
  1798.              (pWave->nAvgBytesPerSec != pWave->nSamplesPerSec * pWave->nBlockAlign) ||
  1799.              !CheckPCMParams((pWave->wBitsPerSample == 8), pWave->nChannels, pWave->nSamplesPerSec))
  1800.             return DMO_E_TYPE_NOT_ACCEPTED;
  1801.       }
  1802.       return NOERROR;
  1803.    }
  1804.  
  1805. protected:
  1806.    // format info - the derived class may look at these (but no modify)
  1807.    ULONG m_ulSamplingRate;
  1808.    ULONG m_cChannels;
  1809.    BOOL m_b8bit;
  1810. };
  1811.  
  1812. //
  1813. // CGenericDMO - generic DMO base class.  This is currently the only base
  1814. // class for DMOs that have multiple inputs or multiple outputs.
  1815. //
  1816. // This base class tries to be reasonably generic.  The derived class reports
  1817. // how many streams it supports and describes each stream by calling
  1818. // CreateInputStreams() and CreateOutputStreams().  Each of these functions
  1819. // takes an array of STREAMDESCRIPTOR structures, each of which poits to an
  1820. // array of FORMATENTRY structures.
  1821. //
  1822. // This base class uses CInputStream and COutputStream classes (both derived
  1823. // from CStream) to keep track of input and output stream.  However, these
  1824. // objects are not visible to the derived class - the derived class only sees
  1825. // stream IDs.
  1826. //
  1827. // One limitation of the scheme use here is that the derived class cannot
  1828. // override the GetType/SetType methods individually for each stream.  It must
  1829. // either (a) live with a static, finite set of types communicated via the
  1830. // STREAMDESCRIPTOR structure, or (b) override all IMediaObject type methods
  1831. // and handle type negotiation for all streams itself.
  1832. //
  1833. // Processing occurs when the base class calles DoProcess (overridden by the
  1834. // derived class).  DoProcess receives an array of input buffer structs and
  1835. // an array of output buffer structs.  The base class takes care of talking
  1836. // to IMediaBuffers, so the derived class only sees actual data pointers.
  1837. //
  1838.  
  1839. // flags used to communicate with the derived class
  1840. enum _INPUT_STATUS_FLAGS {
  1841.    INPUT_STATUSF_RESIDUAL // cannot be further processed w/o additional input
  1842. };
  1843.  
  1844. // These are used to pass buffers between this class and the derived class.
  1845. typedef struct _INPUTBUFFER {
  1846.    BYTE *pData;                 // [in] - if NULL, the rest are garbage
  1847.    DWORD cbSize;                // [in]
  1848.    DWORD cbUsed;                // [out]
  1849.    DWORD dwFlags;               // [in] - DMO_INPUT_DATA_BUFFERF_XXX
  1850.    DWORD dwStatus;              // [out] - INPUT_STATUSF_XXX from above
  1851.    REFERENCE_TIME rtTimestamp;  // [in]
  1852.    REFERENCE_TIME rtTimelength; // [in]
  1853. } INPUTBUFFER, *PINPUTBUFFER;
  1854. typedef struct _OUTPUTBUFFER {
  1855.    BYTE *pData;                 // [in]
  1856.    DWORD cbSize;                // [in]
  1857.    DWORD cbUsed;                // [out]
  1858.    DWORD dwFlags;               // [out] - DMO_OUTPUT_DATA_BUFFERF_XXX
  1859.    REFERENCE_TIME rtTimestamp;  // [out]
  1860.    REFERENCE_TIME rtTimelength; // [out]
  1861. } OUTPUTBUFFER, *POUTPUTBUFFER;
  1862.  
  1863. // Used by derived class to describe the format supported by each stream
  1864. typedef struct _FORMATENTRY
  1865. {
  1866.     const GUID *majortype;
  1867.     const GUID *subtype;
  1868.     const GUID *formattype;
  1869.     DWORD cbFormat;
  1870.     BYTE* pbFormat;
  1871. } FORMATENTRY;
  1872.  
  1873. // These are used by the derived class to described its streams
  1874. typedef struct _INPUTSTREAMDESCRIPTOR {
  1875.    DWORD        cFormats;
  1876.    FORMATENTRY *pFormats;
  1877.    DWORD        dwMinBufferSize;
  1878.    BOOL         bHoldsBuffers;
  1879.    DWORD        dwMaxLookahead; // used if HOLDS_BUFFERS set
  1880. } INPUTSTREAMDESCRIPTOR;
  1881. typedef struct _OUTPUTSTREAMDESCRIPTOR {
  1882.    DWORD        cFormats;
  1883.    FORMATENTRY *pFormats;
  1884.    DWORD        dwMinBufferSize;
  1885. } OUTPUTSTREAMDESCRIPTOR;
  1886.  
  1887. // Common input/output stream stuff
  1888. class CStream {
  1889. public:
  1890.     DMO_MEDIA_TYPE       m_MediaType;
  1891.     BOOL                m_bEOS;
  1892.     BOOL                m_bTypeSet;
  1893.  
  1894.     DWORD        m_cFormats;
  1895.     FORMATENTRY *m_pFormats;
  1896.     DWORD        m_dwMinBufferSize;
  1897.  
  1898.     //  Should really pass in a format type list
  1899.     CStream()
  1900.     {
  1901.         MoInitMediaType(&m_MediaType, 0);
  1902.         m_bTypeSet = FALSE;
  1903.         Flush();
  1904.     }
  1905.     ~CStream()
  1906.     {
  1907.         MoFreeMediaType(&m_MediaType);
  1908.     }
  1909.     HRESULT Flush() {
  1910.        m_bEOS = FALSE;
  1911.        return NOERROR;
  1912.     }
  1913.     HRESULT StreamInfo(unsigned long *pdwFlags)
  1914.     {
  1915.        if (pdwFlags == NULL) {
  1916.            return E_POINTER;
  1917.        }
  1918.        *pdwFlags = 0;
  1919.        return S_OK;
  1920.     }
  1921.     HRESULT GetType(ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt)
  1922.     {
  1923.         if (ulTypeIndex >= m_cFormats) {
  1924.             return E_INVALIDARG;
  1925.         }
  1926.         //  Just return our types
  1927.         MoInitMediaType(pmt, m_pFormats[ulTypeIndex].cbFormat);
  1928.         pmt->majortype  = *m_pFormats[ulTypeIndex].majortype;
  1929.         pmt->subtype    = *m_pFormats[ulTypeIndex].subtype;
  1930.         pmt->formattype = *m_pFormats[ulTypeIndex].formattype;
  1931.         memcpy(pmt->pbFormat, m_pFormats[ulTypeIndex].pbFormat, m_pFormats[ulTypeIndex].cbFormat);
  1932.         return S_OK;
  1933.     }
  1934.     HRESULT GetCurrentType(DMO_MEDIA_TYPE *pmt)
  1935.     {
  1936.         if (NULL == pmt) {
  1937.             return E_POINTER;
  1938.         }
  1939.  
  1940.         if (m_bTypeSet) {
  1941.            //  check success
  1942.            MoCopyMediaType(pmt, &(m_MediaType));
  1943.            return S_OK;
  1944.         }
  1945.         else
  1946.            return DMO_E_TYPE_NOT_SET;
  1947.     }
  1948.     HRESULT SetType(const DMO_MEDIA_TYPE *pmt, DWORD dwFlags)
  1949.     {
  1950.         //  Need to check this
  1951.         HRESULT hr = CheckType(pmt, 0);
  1952.         if (FAILED(hr)) {
  1953.             return hr;
  1954.         }
  1955.         if (dwFlags & DMO_SET_TYPEF_TEST_ONLY) {
  1956.            return NOERROR; // check konly
  1957.         }
  1958.         //  check success
  1959.         MoCopyMediaType(&m_MediaType, pmt);
  1960.  
  1961.         m_bTypeSet = TRUE;;
  1962.         return S_OK;
  1963.     }
  1964.     HRESULT CheckType(const DMO_MEDIA_TYPE *pmt, DWORD dwFlags)
  1965.     {
  1966.         if (pmt == NULL) {
  1967.             return E_POINTER;
  1968.         }
  1969.         //if (dwFlags & ~DMO_SET_TYPEF_NOT_PARTIAL)
  1970.         //    return E_INVALIDARG;
  1971.  
  1972.         //  Default - check GUIDs
  1973.  
  1974.         bool bMatched = false;
  1975.         for (DWORD i = 0; i < m_cFormats; i++) {
  1976.             const FORMATENTRY *pFormat = &(m_pFormats[i]);
  1977.             if (pmt->majortype  == *(pFormat->majortype) &&
  1978.                 pmt->subtype    == *(pFormat->subtype) &&
  1979.                 pmt->formattype == *(pFormat->formattype)) {
  1980.                 bMatched = true;
  1981.                 break;
  1982.             }
  1983.         }
  1984.  
  1985.         if (bMatched) {
  1986.             return S_OK;
  1987.         } else {
  1988.             return DMO_E_INVALIDTYPE;
  1989.         }
  1990.     }
  1991.     HRESULT SizeInfo(ULONG *plSize, ULONG *plAlignment)
  1992.     {
  1993.         if (plSize == NULL || plAlignment == NULL) {
  1994.             return E_POINTER;
  1995.         }
  1996.  
  1997.         *plAlignment = 1;
  1998.         *plSize      = m_dwMinBufferSize;
  1999.         return S_OK;
  2000.     }
  2001. };
  2002.  
  2003. // Input stream specific stuff
  2004. class CInputStream : public CStream {
  2005. public:
  2006.     BOOL         m_bHoldsBuffers;
  2007.     DWORD        m_dwMaxLookahead; // used if HOLDS_BUFFERS set
  2008.  
  2009.     // Current input sample
  2010.     IMediaBuffer *m_pMediaBuffer;
  2011.     DWORD m_dwFlags; // discontinuity, etc.
  2012.     REFERENCE_TIME m_rtTimestamp;
  2013.     REFERENCE_TIME m_rtTimelength;
  2014.     BYTE *m_pData;
  2015.     DWORD m_cbSize;
  2016.     DWORD m_cbUsed;
  2017.  
  2018.     // residual
  2019.     BYTE *m_pbResidual;
  2020.     DWORD m_cbResidual;
  2021.     DWORD m_cbResidualBuffer;
  2022.  
  2023.     // temporary buffer for handling the residual
  2024.     BYTE *m_pbTemp;
  2025.  
  2026.    HRESULT Flush() {
  2027.       if (m_pMediaBuffer) {
  2028.          m_pMediaBuffer->Release();
  2029.          m_pMediaBuffer = NULL;
  2030.       }
  2031.       return CStream::Flush();
  2032.    }
  2033.    CInputStream() {
  2034.       m_pMediaBuffer = NULL;
  2035.       m_pbResidual = NULL;
  2036.       m_pbTemp = NULL;
  2037.    }
  2038.    ~CInputStream() {
  2039.       if (m_pMediaBuffer)
  2040.          m_pMediaBuffer->Release();
  2041.       if (m_pbResidual)
  2042.          delete[] m_pbResidual;
  2043.    }
  2044.    HRESULT StreamInfo(DWORD *pdwFlags) {
  2045.       HRESULT hr = CStream::StreamInfo(pdwFlags);
  2046.       if (FAILED(hr))
  2047.          return hr;
  2048.       if (m_bHoldsBuffers)
  2049.          *pdwFlags |= DMO_INPUT_STREAMF_HOLDS_BUFFERS;
  2050.       return NOERROR;
  2051.    }
  2052.    HRESULT Init(INPUTSTREAMDESCRIPTOR *pDescriptor) {
  2053.       m_cFormats = pDescriptor->cFormats;
  2054.       m_pFormats = pDescriptor->pFormats;
  2055.       m_dwMinBufferSize = pDescriptor->dwMinBufferSize;
  2056.       m_bHoldsBuffers = pDescriptor->bHoldsBuffers;
  2057.       m_dwMaxLookahead = pDescriptor->dwMaxLookahead;
  2058.  
  2059.       // Just in case Init is called multiple times:
  2060.       // delete any preexisting stuff.
  2061.       if (m_pMediaBuffer) {
  2062.          m_pMediaBuffer->Release();
  2063.          m_pMediaBuffer = NULL;
  2064.       }
  2065.       if (m_pbResidual) {
  2066.          delete[] m_pbResidual;
  2067.          m_pbResidual = NULL;
  2068.       }
  2069.  
  2070.       m_cbResidual = 0;
  2071.       m_cbResidualBuffer = m_dwMinBufferSize * 2; // enough ?
  2072.       m_pbResidual = new BYTE[m_cbResidualBuffer];
  2073.  
  2074.       return NOERROR;
  2075.    }
  2076.    HRESULT InputStatus(DWORD *pdwStatus) {
  2077.        // objects that hold buffers must implement InputStatus themselves
  2078.       assert(!m_bHoldsBuffers);
  2079.       *pdwStatus = 0;
  2080.       if (!m_pMediaBuffer)
  2081.          *pdwStatus |= DMO_INPUT_STATUSF_ACCEPT_DATA;
  2082.       return NOERROR;
  2083.    }
  2084.    HRESULT Deliver(
  2085.       IMediaBuffer *pBuffer, // [in], must not be NULL
  2086.       DWORD dwFlags, // [in] - discontinuity, timestamp, etc.
  2087.       REFERENCE_TIME rtTimestamp, // [in], valid if flag set
  2088.       REFERENCE_TIME rtTimelength // [in], valid if flag set
  2089.    ) {
  2090.       if (!pBuffer)
  2091.          return E_POINTER;
  2092.        // objects that hold buffers must implement Deliver themselves
  2093.       assert(!m_bHoldsBuffers);
  2094.       DWORD dwStatus = 0;
  2095.       InputStatus(&dwStatus);
  2096.       if (!(dwStatus & DMO_INPUT_STATUSF_ACCEPT_DATA))
  2097.          return DMO_E_NOTACCEPTING;
  2098.       assert(!m_pMediaBuffer); // can't hold multiple buffers
  2099.  
  2100.       //Deal with the IMediaBuffer
  2101.       HRESULT hr;
  2102.       hr = pBuffer->GetBufferAndLength(&m_pData, &m_cbSize);
  2103.       if (FAILED(hr))
  2104.          return hr;
  2105.  
  2106.       if (!m_cbSize) // empty buffer
  2107.          return S_FALSE; // no data
  2108.  
  2109.       pBuffer->AddRef();
  2110.       m_pMediaBuffer = pBuffer;
  2111.       m_dwFlags = dwFlags;
  2112.       m_rtTimestamp = rtTimestamp;
  2113.       m_rtTimelength = rtTimelength;
  2114.       m_cbUsed = 0;
  2115.       return NOERROR;
  2116.    }
  2117.  
  2118.    //
  2119.    // Fetch data from the currently held IMediaBuffer plus any residual
  2120.    //
  2121.    HRESULT PrepareInputBuffer(INPUTBUFFER *pBuffer)
  2122.    {
  2123.       // Q.: do we even have any data to give it ?
  2124.       if (m_pMediaBuffer) {
  2125.          // Is there a residual we need to feed first ?
  2126.          if (m_cbResidual) {
  2127.             // Yes, prepend the residual to the new input
  2128.  
  2129.             // If we have used some of the input buffer by now, we
  2130.             // should have also used up any residual with that.
  2131.             assert(m_cbUsed == 0);
  2132.  
  2133.             // compute how many bytes total we are going to send
  2134.             pBuffer->cbSize = m_cbResidual
  2135.                                       + m_cbSize;
  2136.  
  2137.             // Make sure we have at least dwMinBufferSize bytes of data.
  2138.             // We really should - the input buffer alone ought to be at
  2139.             // least that big.
  2140.             assert(pBuffer->cbSize > m_dwMinBufferSize);
  2141.  
  2142.             // Is the residual buffer big enough to hold the residual plus
  2143.             // all of the new buffer ?
  2144.             if (pBuffer->cbSize <= m_cbResidualBuffer) {
  2145.                // Yes - wonderful, we can use the residual buffer
  2146.                memcpy(m_pbResidual + m_cbResidual,
  2147.                       m_pData,
  2148.                       m_cbSize);
  2149.                pBuffer->pData = m_pbResidual;
  2150.             }
  2151.             else {
  2152.                // No - allocate a sufficiently large temporary buffer.
  2153.                // This is supposed to be a rare case.
  2154.                m_pbTemp = new BYTE[pBuffer->cbSize];
  2155.                if (m_pbTemp == NULL)
  2156.                   return E_OUTOFMEMORY;
  2157.                // copy the residual
  2158.                memcpy(m_pbTemp,
  2159.                       m_pbResidual,
  2160.                       m_cbResidual);
  2161.                // append the new buffer
  2162.                memcpy(m_pbTemp + m_cbResidual,
  2163.                       m_pData,
  2164.                       m_cbSize);
  2165.  
  2166.                // set the buffer pointer to our temp buffer
  2167.                pBuffer->pData = m_pbTemp;
  2168.             }
  2169.  
  2170.             // is this the correct way to handle timestamps &
  2171.             // discontinuities when handling a residual ?
  2172.             pBuffer->dwFlags = 0;
  2173.          }
  2174.          else { // no residual
  2175.             pBuffer->pData = m_pData + m_cbUsed;
  2176.             pBuffer->cbSize = m_cbSize - m_cbUsed;
  2177.             pBuffer->dwFlags = m_dwFlags;
  2178.             pBuffer->rtTimestamp = m_rtTimestamp;
  2179.             pBuffer->rtTimelength= m_rtTimelength;
  2180.          }
  2181.          pBuffer->cbUsed = 0; // derived class should set this
  2182.          pBuffer->dwStatus = 0; // derived class should set this
  2183.       }
  2184.       else {
  2185.          pBuffer->pData = NULL;
  2186.          pBuffer->cbSize = 0;
  2187.       }
  2188.       return NOERROR;
  2189.    }
  2190.  
  2191.    //
  2192.    // Save any residual and release the IMediaBuffer as appropriate.
  2193.    // Returns TRUE if there is enough data left to call ProcesInput again.
  2194.    //
  2195.    BOOL PostProcessInputBuffer(INPUTBUFFER *pBuffer)
  2196.    {
  2197.       BOOL bRet = FALSE;
  2198.       // did we even give this stream anything ?
  2199.       if (m_pMediaBuffer) {
  2200.          // Yes, but did it eat any of it ?
  2201.          if (pBuffer->cbUsed) {
  2202.             // Did we even get past the residual
  2203.             if (pBuffer->cbUsed > m_cbResidual) {
  2204.                // Yes - reflect this in the current buffer's cbUsed.
  2205.                m_cbUsed += (pBuffer->cbUsed - m_cbResidual);
  2206.                m_cbResidual = 0;
  2207.             }
  2208.             else {
  2209.                // No - just subtract from the residual.
  2210.                m_cbResidual -= pBuffer->cbUsed;
  2211.                memmove(m_pbResidual,
  2212.                        m_pbResidual + pBuffer->cbUsed,
  2213.                        m_cbResidual);
  2214.             }
  2215.          }
  2216.  
  2217.          // Is there enough left to feed again the next time ?
  2218.          if ((m_cbSize - m_cbUsed <
  2219.               m_dwMinBufferSize)
  2220.               || (pBuffer->dwStatus & INPUT_STATUSF_RESIDUAL)) {
  2221.             // No - copy the residual and release the buffer
  2222.             memcpy(m_pbResidual,
  2223.                    m_pData + m_cbUsed,
  2224.                    m_cbSize - m_cbUsed);
  2225.             m_cbResidual
  2226.               = pBuffer->cbSize - pBuffer->cbUsed;
  2227.             m_pMediaBuffer->Release();
  2228.             m_pMediaBuffer = NULL;
  2229.          }
  2230.          else { // Yes - need another Process call to eat remaining input
  2231.             bRet = TRUE;
  2232.          }
  2233.  
  2234.          // Free any temporary buffer we may have used - rare case
  2235.          if (m_pbTemp) {
  2236.             delete[] m_pbTemp;
  2237.             m_pbTemp = NULL;
  2238.          }
  2239.       }
  2240.       return bRet;
  2241.    }
  2242.    HRESULT Discontinuity() {
  2243.       // implement
  2244.       // m_bDiscontinuity = TRUE;
  2245.       return NOERROR;
  2246.    }
  2247.    HRESULT SizeInfo(ULONG *pulSize,
  2248.                     ULONG *pulMaxLookahead,
  2249.                     ULONG *pulAlignment) {
  2250.       HRESULT hr = CStream::SizeInfo(pulSize, pulAlignment);
  2251.       if (FAILED(hr))
  2252.          return hr;
  2253.  
  2254.       if (m_bHoldsBuffers)
  2255.          *pulMaxLookahead = m_dwMaxLookahead;
  2256.       else
  2257.          *pulMaxLookahead = *pulSize;
  2258.       return NOERROR;
  2259.    }
  2260. };
  2261.  
  2262. // Output stream specific stuff
  2263. class COutputStream : public CStream {
  2264. public:
  2265.    BOOL m_bIncomplete;
  2266.    DWORD m_cbAlreadyUsed; // temp per-stream variable used during Process
  2267.  
  2268.    HRESULT Init(OUTPUTSTREAMDESCRIPTOR *pDescriptor) {
  2269.       m_cFormats = pDescriptor->cFormats;
  2270.       m_pFormats = pDescriptor->pFormats;
  2271.       m_dwMinBufferSize = pDescriptor->dwMinBufferSize;
  2272.       return NOERROR;
  2273.    }
  2274.  
  2275.    //
  2276.    // Initialize the OUTPUTBUFFER struct with info from the IMediaBuffer
  2277.    //
  2278.    HRESULT PrepareOutputBuffer(OUTPUTBUFFER *pBuffer, IMediaBuffer *pMediaBuffer, BOOL bNewInput)
  2279.    {
  2280.       //
  2281.       // See if the caller supplied an output buffer
  2282.       //
  2283.       if (pMediaBuffer == NULL) {
  2284.          // This is allowed to be NULL only if (1) the object did not set
  2285.          // the INCOMPLETE flag for this stream during the last Process
  2286.          // call, and (2) no new input data has been supplied to the object
  2287.          // since the last Process call.
  2288.          if (bNewInput)
  2289.             return E_POINTER;
  2290.          if (m_bIncomplete)
  2291.             return E_POINTER;
  2292.  
  2293.          // ok - initialize assuming no buffer
  2294.          pBuffer->cbSize = 0;
  2295.          pBuffer->pData = NULL;
  2296.       }
  2297.       else { // the IMediaBuffer is not NULL - deal with it
  2298.          HRESULT hr;
  2299.          hr = pMediaBuffer->GetMaxLength(&pBuffer->cbSize);
  2300.          if (FAILED(hr))
  2301.             return hr;
  2302.  
  2303.          hr = pMediaBuffer->GetBufferAndLength(
  2304.                  &(pBuffer->pData),
  2305.                  &(m_cbAlreadyUsed));
  2306.          if (FAILED(hr))
  2307.             return hr;
  2308.  
  2309.          // Check current size - should we even bother with this ?
  2310.          if (m_cbAlreadyUsed) {
  2311.             if (m_cbAlreadyUsed >= pBuffer->cbSize)
  2312.                return E_INVALIDARG; // buffer already full ?!?
  2313.             pBuffer->cbSize -= m_cbAlreadyUsed;
  2314.             pBuffer->pData += m_cbAlreadyUsed;
  2315.          }
  2316.       }
  2317.  
  2318.       // It is really the derived class's job to set these, but we
  2319.       // will be nice to it and initialize them anyway just in case.
  2320.       pBuffer->cbUsed = 0;
  2321.       pBuffer->dwFlags = 0;
  2322.  
  2323.       return NOERROR;
  2324.    }
  2325.  
  2326.    //
  2327.    // Copy the OUTPUTBUFFER back into the DMO_OUTPUT_DATA_BUFFER (yawn)
  2328.    //
  2329.    void PostProcessOutputBuffer(OUTPUTBUFFER *pBuffer, DMO_OUTPUT_DATA_BUFFER *pDMOBuffer, BOOL bForceIncomplete) {
  2330.       assert(pBuffer->cbUsed <= pBuffer->cbSize);
  2331.       if (pDMOBuffer->pBuffer)
  2332.          pDMOBuffer->pBuffer->SetLength(pBuffer->cbUsed + m_cbAlreadyUsed);
  2333.       pDMOBuffer->dwStatus = pBuffer->dwFlags;
  2334.       pDMOBuffer->rtTimestamp = pBuffer->rtTimestamp;
  2335.       pDMOBuffer->rtTimelength = pBuffer->rtTimelength;
  2336.  
  2337.       // Even if the derived class did not set INCOMPLETE, we may need to
  2338.       // set it anyway if some input buffer we are holding still has
  2339.       // enough data to call Process() again.
  2340.       if (bForceIncomplete)
  2341.          pDMOBuffer->dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
  2342.  
  2343.       // remember this output stream's INCOMPLETE state
  2344.       if (pDMOBuffer->dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE)
  2345.          m_bIncomplete = TRUE;
  2346.       else
  2347.          m_bIncomplete = FALSE;
  2348.    }
  2349. };
  2350.  
  2351. // Code that goes at the beginning of every IMediaObject method
  2352. #define INPUT_STREAM_PROLOGUE \
  2353.     CDMOAutoLock l(&m_cs); \
  2354.     if (ulInputStreamIndex >= m_nInputStreams) \
  2355.        return DMO_E_INVALIDSTREAMINDEX; \
  2356.     CInputStream *pStream = &m_pInputStreams[ulInputStreamIndex]
  2357.  
  2358. #define OUTPUT_STREAM_PROLOGUE \
  2359.     CDMOAutoLock l(&m_cs); \
  2360.     if (ulOutputStreamIndex >= m_nOutputStreams) \
  2361.        return DMO_E_INVALIDSTREAMINDEX; \
  2362.     COutputStream *pStream = &m_pOutputStreams[ulOutputStreamIndex]
  2363.  
  2364.  
  2365. class CGenericDMO : public IMediaObject
  2366. {
  2367. public:
  2368.     CGenericDMO() {
  2369. #ifdef DMO_NOATL
  2370.        InitializeCriticalSection(&m_cs);
  2371. #endif
  2372.        m_nInputStreams = 0;
  2373.        m_nOutputStreams = 0;
  2374.     }
  2375. #ifdef DMO_NOATL
  2376.     ~CGenericDMO() {
  2377.        DeleteCriticalSection(&m_cs);
  2378.     }
  2379. #endif
  2380.  
  2381. public:
  2382.     //
  2383.     // Implement IMediaObject methods
  2384.     //
  2385.     STDMETHODIMP GetInputStreamInfo(ULONG ulInputStreamIndex, DWORD *pdwFlags)
  2386.     {
  2387.        INPUT_STREAM_PROLOGUE;
  2388.        return pStream->StreamInfo(pdwFlags);
  2389.     }
  2390.     STDMETHODIMP GetOutputStreamInfo(ULONG ulOutputStreamIndex, DWORD *pdwFlags)
  2391.     {
  2392.        OUTPUT_STREAM_PROLOGUE;
  2393.        return pStream->StreamInfo(pdwFlags);
  2394.     }
  2395.     STDMETHODIMP GetInputType(ULONG ulInputStreamIndex, ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  2396.        INPUT_STREAM_PROLOGUE;
  2397.        return pStream->GetType(ulTypeIndex, pmt);
  2398.     }
  2399.     STDMETHODIMP GetOutputType(ULONG ulOutputStreamIndex, ULONG ulTypeIndex, DMO_MEDIA_TYPE *pmt) {
  2400.        OUTPUT_STREAM_PROLOGUE;
  2401.        return pStream->GetType(ulTypeIndex, pmt);
  2402.     }
  2403.     STDMETHODIMP GetInputCurrentType(ULONG ulInputStreamIndex, DMO_MEDIA_TYPE *pmt) {
  2404.        INPUT_STREAM_PROLOGUE;
  2405.        return pStream->GetCurrentType(pmt);
  2406.     }
  2407.     STDMETHODIMP GetOutputCurrentType(ULONG ulOutputStreamIndex, DMO_MEDIA_TYPE *pmt) {
  2408.        OUTPUT_STREAM_PROLOGUE;
  2409.        return pStream->GetCurrentType(pmt);
  2410.     }
  2411.     STDMETHODIMP GetInputSizeInfo(ULONG ulInputStreamIndex, ULONG *pulSize, ULONG *pcbMaxLookahead, ULONG *pulAlignment) {
  2412.        INPUT_STREAM_PROLOGUE;
  2413.        return pStream->SizeInfo(pulSize, pcbMaxLookahead, pulAlignment);
  2414.     }
  2415.     STDMETHODIMP GetOutputSizeInfo(ULONG ulOutputStreamIndex, ULONG *pulSize, ULONG *pulAlignment) {
  2416.        OUTPUT_STREAM_PROLOGUE;
  2417.        return pStream->SizeInfo(pulSize, pulAlignment);
  2418.     }
  2419.     STDMETHODIMP SetInputType(ULONG ulInputStreamIndex, const DMO_MEDIA_TYPE *pmt, DWORD dwFlags) {
  2420.        INPUT_STREAM_PROLOGUE;
  2421.        return pStream->SetType(pmt, dwFlags);
  2422.     }
  2423.     STDMETHODIMP SetOutputType(ULONG ulOutputStreamIndex, const DMO_MEDIA_TYPE *pmt, DWORD dwFlags) {
  2424.        OUTPUT_STREAM_PROLOGUE;
  2425.        return pStream->SetType(pmt, dwFlags);
  2426.     }
  2427.     STDMETHODIMP GetInputStatus(
  2428.         ULONG ulInputStreamIndex,
  2429.         DWORD *pdwStatus
  2430.     ) {
  2431.        INPUT_STREAM_PROLOGUE;
  2432.        return pStream->InputStatus(pdwStatus);
  2433.     }
  2434.     STDMETHODIMP GetInputMaxLatency(unsigned long ulInputStreamIndex, REFERENCE_TIME *prtLatency) {
  2435.        // I don't know what to do with this right now.
  2436.        // Punt to the derived class ?
  2437.        return E_NOTIMPL;
  2438.     }
  2439.     STDMETHODIMP SetInputMaxLatency(unsigned long ulInputStreamIndex, REFERENCE_TIME rtLatency) {
  2440.        return E_NOTIMPL;
  2441.     }
  2442.     STDMETHODIMP ProcessInput(
  2443.         DWORD ulInputStreamIndex,
  2444.         IMediaBuffer *pBuffer, // [in], must not be NULL
  2445.         DWORD dwFlags, // [in] - discontinuity, timestamp, etc.
  2446.         REFERENCE_TIME rtTimestamp, // [in], valid if flag set
  2447.         REFERENCE_TIME rtTimelength // [in], valid if flag set
  2448.     ) {
  2449.        INPUT_STREAM_PROLOGUE;
  2450.        return pStream->Deliver(pBuffer, dwFlags, rtTimestamp, rtTimelength);
  2451.     }
  2452.     STDMETHODIMP Discontinuity(ULONG ulInputStreamIndex) {
  2453.        INPUT_STREAM_PROLOGUE;
  2454.        return pStream->Discontinuity();
  2455.     }
  2456.  
  2457.     STDMETHODIMP Flush()
  2458.     {
  2459.        CDMOAutoLock l(&m_cs);
  2460.  
  2461.        //  Flush all the streams
  2462.        ULONG i;
  2463.        for (i = 0; i < m_nInputStreams; i++) {
  2464.           m_pInputStreams[i].Flush();
  2465.        }
  2466.        for (i = 0; i < m_nOutputStreams; i++) {
  2467.           m_pOutputStreams[i].Flush();
  2468.        }
  2469.        return S_OK;
  2470.     }
  2471.  
  2472.     STDMETHODIMP AllocateStreamingResources() {return S_OK;}
  2473.     STDMETHODIMP FreeStreamingResources() {return S_OK;}
  2474.  
  2475.     STDMETHODIMP GetStreamCount(unsigned long *pulNumberOfInputStreams, unsigned long *pulNumberOfOutputStreams)
  2476.     {
  2477.         CDMOAutoLock l(&m_cs);
  2478.         if (pulNumberOfInputStreams == NULL ||
  2479.             pulNumberOfOutputStreams == NULL) {
  2480.             return E_POINTER;
  2481.         }
  2482.         *pulNumberOfInputStreams = m_nInputStreams;
  2483.         *pulNumberOfOutputStreams = m_nOutputStreams;
  2484.         return S_OK;
  2485.     }
  2486.  
  2487.     STDMETHODIMP ProcessOutput(
  2488.                     DWORD dwReserved,
  2489.                     DWORD ulOutputBufferCount,
  2490.                     DMO_OUTPUT_DATA_BUFFER *pOutputBuffers,
  2491.                     DWORD *pdwStatus)
  2492.     {
  2493.        CDMOAutoLock l(&m_cs);
  2494.        if (ulOutputBufferCount != m_nOutputStreams)
  2495.           return E_INVALIDARG;
  2496.  
  2497.        HRESULT hr;
  2498.        DWORD c;
  2499.  
  2500.        // Prepare the input buffers
  2501.        for (c = 0; c < m_nInputStreams; c++) {
  2502.           // objects that hold buffers must implement Process themselves
  2503.           assert(!m_pInputStreams[c].m_bHoldsBuffers);
  2504.           hr = m_pInputStreams[c].PrepareInputBuffer(&m_pInputBuffers[c]);
  2505.           if (FAILED(hr))
  2506.              return hr;
  2507.        }
  2508.  
  2509.        //
  2510.        // Prepare the output buffers
  2511.        //
  2512.        for (c = 0; c < m_nOutputStreams; c++) {
  2513.           hr = m_pOutputStreams[c].PrepareOutputBuffer(&m_pOutputBuffers[c], pOutputBuffers[c].pBuffer, m_bNewInput);
  2514.           if (FAILED(hr))
  2515.              return hr;
  2516.        }
  2517.  
  2518.        hr = DoProcess(m_pInputBuffers,m_pOutputBuffers);
  2519.        if (FAILED(hr))
  2520.           return hr; // don't just "return hr", do something !
  2521.  
  2522.        // post-process input buffers
  2523.        BOOL bSomeInputStillHasData = FALSE;
  2524.        for (c = 0; c < m_nInputStreams; c++) {
  2525.           if (m_pInputStreams[c].PostProcessInputBuffer(&m_pInputBuffers[c]))
  2526.              bSomeInputStillHasData = TRUE;
  2527.        }
  2528.  
  2529.        // post-process output buffers
  2530.        for (c = 0; c < m_nOutputStreams; c++) {
  2531.           m_pOutputStreams[c].PostProcessOutputBuffer(&m_pOutputBuffers[c],
  2532.                                                       &pOutputBuffers[c],
  2533.                                                       bSomeInputStillHasData);
  2534.        }
  2535.  
  2536.        m_bNewInput = FALSE;
  2537.        return NOERROR;
  2538.     }
  2539.  
  2540. protected:
  2541.     //
  2542.     // These are called by the derived class at initialization time
  2543.     //
  2544.     HRESULT CreateInputStreams(INPUTSTREAMDESCRIPTOR *pStreams, DWORD cStreams) {
  2545.        CDMOAutoLock l(&m_cs);
  2546.        if (pStreams == NULL) {
  2547.           return E_POINTER;
  2548.        }
  2549.  
  2550.        m_pInputStreams = new CInputStream[cStreams];
  2551.  
  2552.        if (m_pInputStreams == NULL) {
  2553.           return E_OUTOFMEMORY;
  2554.        }
  2555.  
  2556.        DWORD c;
  2557.        for (c = 0; c < cStreams; c++) {
  2558.           HRESULT hr = m_pInputStreams[c].Init(&(pStreams[c]));
  2559.           if (FAILED(hr)) {
  2560.              delete[] m_pInputStreams;
  2561.              return hr;
  2562.           }
  2563.        }
  2564.  
  2565.        m_pInputBuffers = new INPUTBUFFER[cStreams];
  2566.        if (!m_pInputBuffers) {
  2567.           delete[] m_pInputStreams;
  2568.           return E_OUTOFMEMORY;
  2569.        }
  2570.  
  2571.        m_nInputStreams = cStreams;
  2572.        return NOERROR;
  2573.     }
  2574.     HRESULT CreateOutputStreams(OUTPUTSTREAMDESCRIPTOR *pStreams, DWORD cStreams) {
  2575.        CDMOAutoLock l(&m_cs);
  2576.        if (pStreams == NULL) {
  2577.           return E_POINTER;
  2578.        }
  2579.  
  2580.        m_pOutputStreams = new COutputStream[cStreams];
  2581.  
  2582.        if (m_pOutputStreams == NULL) {
  2583.           return E_OUTOFMEMORY;
  2584.        }
  2585.  
  2586.        DWORD c;
  2587.        for (c = 0; c < cStreams; c++) {
  2588.           HRESULT hr = m_pOutputStreams[c].Init(&(pStreams[c]));
  2589.           if (FAILED(hr)) {
  2590.              delete[] m_pOutputStreams;
  2591.              return hr;
  2592.           }
  2593.        }
  2594.     
  2595.        m_pOutputBuffers = new OUTPUTBUFFER[cStreams];
  2596.        if (!m_pOutputBuffers) {
  2597.           delete[] m_pOutputStreams;
  2598.           return E_OUTOFMEMORY;
  2599.        }
  2600.  
  2601.        m_nOutputStreams = cStreams;
  2602.        return NOERROR;
  2603.     }
  2604.  
  2605.     virtual HRESULT DoProcess(INPUTBUFFER*, OUTPUTBUFFER *) = 0;
  2606.  
  2607. private:
  2608.  
  2609.     ULONG           m_nInputStreams;
  2610.     CInputStream*   m_pInputStreams;
  2611.     ULONG           m_nOutputStreams;
  2612.     COutputStream*  m_pOutputStreams;
  2613.  
  2614.     INPUTBUFFER*    m_pInputBuffers;
  2615.     OUTPUTBUFFER*   m_pOutputBuffers;
  2616.  
  2617.     BOOL m_bNewInput;
  2618. #ifdef DMO_NOATL
  2619.     CRITICAL_SECTION m_cs;
  2620. #else
  2621.     CComAutoCriticalSection m_cs;
  2622. #endif
  2623. };
  2624.  
  2625. #endif // __DMOBASE_H__
  2626.